一、类继承关系

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList继承AbstractList,也实现了List和RandomAccess(一个空接口,也就是标记接口。),Cloneable(可被克隆), Serializable接口。

二、类属性

    //默认容量
private static final int DEFAULT_CAPACITY = 10; //空对象数组(initialCapacity == 0时返回)
private static final Object[] EMPTY_ELEMENTDATA = {}; //调用无参构造方法,返回的是该数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //元素数组,保存添加到ArrayList中的元素
transient Object[] elementData; //ArrayList大小
private int size;

elementData是transient修饰,所以elementData不能被序列化。但是ArrayList又是可以被序列化的,这是为何?

  • 因为ArrayList有两个方法writeObject、readObject用于序列化和反序列化。elementData是transient修饰是为了避免ArrayList扩容(1.5倍)导致的空间浪费
  1. 序列化的时候elementData的数据会被写到ObjectOutputStream中
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size); // Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
} if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
  1. 反序列化的时候会从ObjectInputStream读取出来
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff
s.defaultReadObject(); // Read in capacity
s.readInt(); // ignored if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size); Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}

三、构造函数

  • 指定容量
    public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//空对象数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
  • 默认
    public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • ArrayList(Collection<? extends E> c)
    public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//空对象数组
this.elementData = EMPTY_ELEMENTDATA;
}
}

四、ArrayList如何扩容

    public boolean add(E e) {
//确保elementData数组的大小
ensureCapacityInternal(size + 1);
//在结尾处添加元素
elementData[size++] = e;
return true;
}

上面是add(E e)方法的源码,在添加元素前会调用 ensureCapacityInternal(size + 1);确保elementData数组的大小。

    private void ensureCapacityInternal(int minCapacity) {
// 判断元素数组是否为空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//minCapacity最少=DEFAULT_CAPACITY=10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
  • 其中ensureCapacityInternal的源码片段首先判断elementData是否为无参构造器的空数组,如果是的话,Math.max()取较大值赋值给minCapacity,最小是10(默认容量)。然后,调用ensureExplicitCapacity:
    private void ensureExplicitCapacity(int minCapacity) {
// List结构性修改加1
modCount++;
//minCapacity大于elementData数组长度
if (minCapacity - elementData.length > 0)
//增长
grow(minCapacity);
}
  • ensureExplicitCapacity中modCount记录List结构性变化次数(结构性变化,在迭代的过程中可能会造成错误结果)。
  • 紧接着,如果minCapacity大于elementData数组长度则调用 grow(minCapacity)增长函数

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//oldCapacity >> 1右移一位,这里相当于扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}

上面的扩容方法中 oldCapacity >> 1 表明elementData.length右移一位,扩容后的容量为原始容量的1.5倍

五、主要函数

remove()函数
    public E remove(int index) {
rangeCheck(index); modCount++;
E oldValue = elementData(index);
//需要移动的元素个数
int numMoved = size - index - 1;
//删除的不是最后一个元素
if (numMoved > 0)
//index后的元素都向前移动一位
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//size减一,释放引用,方便垃圾回收器进行垃圾回收
elementData[--size] = null; // clear to let GC do its work return oldValue;
}
  • remove(int index)方法会先计算需要移动的元素个数,index后的元素都向前移动一位(调用System.arraycopy方法移动),然后size减一,赋值为null释放引用,方便垃圾回收器进行垃圾回收。
单线程环境下,正确的删除元素,应该使用iterator()迭代器删除元素。

例如要删除集合中的“a”

        Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
if ("a".equals(iterator.next())) {
iterator.remove();
}
}

或者使用lambda表达式

list.removeIf("x"::equals);
set()函数,会替换新值,返回旧值。
    public E set(int index, E element) {
//检查索引
rangeCheck(index); E oldValue = elementData(index);
elementData[index] = element;
//返回旧值
return oldValue;
}
get()函数,获取元素
    public E get(int index) {
rangeCheck(index); return elementData(index);
}

六、ArrayList性能相关

  • 在能够确定ArrayList容量大小的情况下尽量预估容量,调用构造器public ArrayList(int initialCapacity)指定大小,避免因为自动扩容带来的性能开销。
  • 使用trimToSize()使ArrayList的内部数组大小减少为实际大小,以此节省空间
    public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}

【Java源码】集合类-ArrayList的更多相关文章

  1. Java源码之ArrayList

    本文源码均来自Java 8 总体介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类.Set和List两个类继承于它.Set中不能包含重复的元素,也没有顺序来存放. ...

  2. 浅析Java源码之ArrayList

    面试题经常会问到LinkedList与ArrayList的区别,与其背网上的废话,不如直接撸源码! 文章源码来源于JRE1.8,java.util.ArrayList 既然是浅析,就主要针对该数据结构 ...

  3. Java源码阅读ArrayList

    1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAc ...

  4. Java源码之ArrayList分析

    一.ArrayList简介 ArrayList底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据. 与Java中的数组相比,它的容量能动态增长.当创建一个数组的时候,就必须确 ...

  5. Java源码-集合-ArrayList

    基于JDK1.8.0_191 介绍   在Java中,对于数据的保存和使用有多种方式,主要的目的是以更少的资源消耗解决更多的问题,数组就是其中的一种,它的特点是所有的数据都保存在内存的一段连续空间中, ...

  6. Java源码解析——集合框架(一)——ArrayList

    ArrayList源码分析 ArrayList就是动态数组,是Array的复杂版本,它提供了动态的增加和减少元素.灵活的设置数组的大小. 一.类声明 public class ArrayList< ...

  7. Java源码系列1——ArrayList

    本文简单介绍了 ArrayList,并对扩容,添加,删除操作的源代码做分析.能力有限,欢迎指正. ArrayList是什么? ArrayList 就是数组列表,主要用来装载数据.底层实现是数组 Obj ...

  8. Java集合源码剖析——ArrayList源码剖析

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

  9. JDK1.8源码学习-ArrayList

    JDK1.8源码学习-ArrayList 目录 一.ArrayList简介 为了弥补普通数组无法自动扩容的不足,Java提供了集合类,其中ArrayList对数组进行了封装,使其可以自动的扩容或缩小长 ...

  10. Jdk源码-集合类主要原理和解析

    写在前面 熟悉Jdk原理的重要性不言而喻,作为Java开发者或者面试者,了解其实现原理也显得更为装逼,在Java读书计划我写到了,它是面试中最基础的一部分,所以单独拿出来做个总结,为了更好滴理解和学习 ...

随机推荐

  1. AS400服务程序总结

    1.服务程序的创建和调用过程 1.1生成module 1.2编写BND文件确定输出接口 1.3生成服务程序 1.3.运行调用程序时,将服务程序导入到作业内存区active group,常驻内存 2.结 ...

  2. 3.12 在运算和比较时使用NULL值

    问题:NULL值永远不会等于或不等于任何值,也包括NULL值自己,但是需要像计算真实值一样计算可为空列的返回值.例如,需要在表emp中查出所有比“WARD”提成(COMM)低的员工,提成为NULL(空 ...

  3. 关于Java的三种普通排序

    首先要知道是哪几种排序 这里我们所说的是 冒泡排序,选择排序以及插入排序 然后要理解大概的排序速度 : 插入<选择<冒泡 下面是代码 大家可以拷贝自己在java环境里运行运行! publi ...

  4. HTML基础(三)图像和超链接

    图像 img 元素向网页中嵌入一幅图像. 语法 <img src="" alt="" /> img标签常用属性 src 跳转的url alt 图片不 ...

  5. 函数内部属性之arguments和this

    在函数内部,有两个特殊的对象:arguments和this. 1.arguments arguments是一个类数组对象.包含着传入函数中的所有参数.但这个对象还有一个名叫callee的属性,该属性是 ...

  6. dell服务器快速设置idrac

    前提:将服务器专用的idrac网络接口,连接到网络上 1.登录到服务器(即被监控的服务器). 2.安装客户端工具 yum  install  OpenIPMI OpenIPMI-devel OpenI ...

  7. kvm安装图终端界面及形界面安装系统

    1.图形界面安装: qemu-img create -f qcow2 /kvm/os/vm-01.qcow2 16G mkdir -p /kvm/iso cd /kvm/iso 上传事先下载好的镜像文 ...

  8. CSS3---媒体查询与响应式布局

    1. 值 设备类型 All 所有设备 Braille 盲人用点字法触觉回馈设备 Embossed 盲文打印机 Handheld 便携设备 Print 打印用纸或打印预览视图 Projection 各种 ...

  9. 嵩天老师的零基础Python笔记:https://www.bilibili.com/video/av13570243/?from=search&seid=15873837810484552531 中的15-23讲

    #coding=gbk#嵩天老师的零基础Python笔记:https://www.bilibili.com/video/av13570243/?from=search&seid=1587383 ...

  10. 【HIHOCODER 1320】压缩字符串(区间DP)

    描述 小Hi希望压缩一个只包含大写字母'A'-'Z'的字符串.他使用的方法是:如果某个子串 S 连续出现了 X 次,就用'X(S)'来表示.例如AAAAAAAAAABABABCCD可以用10(A)2( ...