JDK源码笔记-ArrayList
1、概述
ArrayList ,基于 [] 数组实现的,支持自动扩容的动态数组。相比数组来说,因为其支持自动扩容的特性,成为我们日常开发中,最常用的集合类,没有之一
2、类图
ArrayList 实现的接口、继承的抽象类

实现了 4 个接口,分别是:
(1)java.util.List 接口,提供数组的添加、删除、修改、迭代遍历等操作
(2)java.util.RandomAccess 接口,表示 ArrayList 支持快速的随机访问
(3)java.io.Serializable 接口,表示 ArrayList 支持序列化的功能
(4)java.lang.Cloneable 接口,表示 ArrayList 支持克隆
继承了 java.util.AbstractList 抽象类,而 AbstractList 提供了 List 接口的骨架实现,大幅度的减少了实现迭代遍历相关操作的代码,不过实际上,ArrayList 大量重写了 AbstractList 提供的方法实现。所以,AbstractList 对于 ArrayList 意义不大,更多的是 AbstractList 其它子类享受了这个福利
3、属性
ArrayList 的属性很少,仅仅 2 个。如下图所示:

(1)elementData 属性:元素数组。其中,图中红色空格代表我们已经添加元素,白色空格代表我们并未使用。
(2)size 属性:数组大小。注意,size 代表的是 ArrayList 已使用 elementData 的元素的数量,对于开发者看到的 #size() 也是该大小。并且,当我们添加新的元素时,恰好其就是元素添加到 elementData 的位置(下标)。当然,我们知道 ArrayList 真正的大小是 elementData 的大小
4、提供的方法
(1)构造方法
  #ArrayList()
/** * 默认初始化容量 * * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * 共享的空数组对象,用于 {@link #ArrayList()} 构造方法。 * * 通过使用该静态变量,和 {@link #EMPTY_ELEMENTDATA} 区分开来,在第一次添加元素时。 * * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
  ArrayList被初始化为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 这个空数组,ArrayList 考虑到节省内存,一些使用场景下仅仅是创建了 ArrayList 对象,实际并未使用。所以,ArrayList 优化成初始化是个空数组,在首次添加元素时,才真正初始化为容量为 10 的数组。
  那么为什么单独声明了 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 空数组,而不直接使用 EMPTY_ELEMENTDATA 呢?在下文中,我们会看到 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 首次扩容为 10 ,而 EMPTY_ELEMENTDATA 按照 1.5 倍扩容从 0 开始而不是 10 
  #ArrayList(int initialCapacity)根据传入的初始化容量,创建 ArrayList 数组。如果我们在使用时,如果预先指到数组大小,一定要使用该构造方法,可以避免数组扩容提升性能,同时也是合理使用内存。
/** * 共享的空数组对象。 * * 在 {@link #ArrayList(int)} 或 {@link #ArrayList(Collection)} 构造方法中, * 如果传入的初始化大小或者集合大小为 0 时,将 {@link #elementData} 指向它。 * * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; public ArrayList(int initialCapacity) { // 初始化容量大于 0 时,创建 Object 数组 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; // 初始化容量等于 0 时,使用 EMPTY_ELEMENTDATA 对象 } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; // 初始化容量小于 0 时,抛出 IllegalArgumentException 异常 } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
  比较特殊的是,如果初始化容量为 0 时,使用 EMPTY_ELEMENTDATA 空数组。在添加元素的时候,会进行扩容创建需要的数组
  #ArrayList(Collection<? extends E> c)使用传入的 c 集合,作为 ArrayList 的 elementData
public ArrayList(Collection<? extends E> c) { // 将 c 转换成 Object 数组 elementData = c.toArray(); // 如果数组大小大于 0 if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) // <X> 如果集合元素不是 Object[] 类型,则会创建新的 Object[] 数组,并将 elementData 赋值到其中,最后赋值给 elementData 。 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); // 如果数组大小等于 0 ,则使用 EMPTY_ELEMENTDATA 。 } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
(2)添加单个元素
  #add(E e) 方法,顺序添加单个元素到数组
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! //设置到末尾并赋值 elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { //判断是否是空参构造时的空数组,如果是则确保最新有最小容量10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { //增加数组修改次数modCount,在父类 AbstractList 上,定义了modCount属性,用于记录数组修改次数。 modCount++; // overflow-conscious code //如果添加一个元素后,容量大于elementData则扩容 if (minCapacity - elementData.length > 0) //扩容待会单独拿出来分析 grow(minCapacity); }
#add(int index, E element),插入单个元素到指定位置
public void add(int index, E element) { //校验是否在数组范围内 rangeCheckForAdd(index); //校验是否扩容 ensureCapacityInternal(size + 1); // Increments modCount!! //将index位置开始的元素往后挪 System.arraycopy(elementData, index, elementData, index + 1, size - index); //插入到指定位置 elementData[index] = element; size++; } private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
(3)数组扩容
  #grow() 方法,扩容数组,并返回它。整个的扩容过程,首先创建一个新的更大的数组,一般是 1.5 倍大小(为什么说是一般呢,稍后我们会看到,会有一些小细节),然后将原数组复制到新数组中,最后返回新数组。
private void grow(int minCapacity) { // 旧数组容量 int oldCapacity = elementData.length; //新数组容量 1.5倍 //oldCapacity >> 1 向右位运算 例如:4 二进制 100 >> 1 = 10 变成 2 int newCapacity = oldCapacity + (oldCapacity >> 1); //确保扩容最小为1.5倍 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //MAX_ARRAY_SIZE = 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); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //四舍五入,保证不超过int的最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
(4)添加多个元素
  #addAll(Collection<? extends E> c) 方法,批量添加多个元素。在我们明确知道会添加多个元素时,推荐使用该该方法而不是添加单个元素,避免可能多次扩容
public boolean addAll(Collection<? extends E> c) { //将c转成数组 Object[] a = c.toArray(); //c的长度 int numNew = a.length; //校验添加这些元素需不需要扩容,保证扩容最小1.5倍 ensureCapacityInternal(size + numNew); // Increments modCount //将c中的元素复制到elementData从size位置开始 System.arraycopy(a, 0, elementData, size, numNew); //数组大小增加c的长度 size += numNew; return numNew != 0; }
(5)从指定位置插入多个元素
  #addAll(int index, Collection<? extends E> c) 方法,从指定位置开始插入多个元素
public boolean addAll(int index, Collection<? extends E> c) { //校验插入位置是否在数组内 rangeCheckForAdd(index); //将想要添加的元素转为数组 Object[] a = c.toArray(); //拿到想要添加元素的长度 int numNew = a.length; //校验是否需要扩容,保证最小扩容1.5倍 ensureCapacityInternal(size + numNew); // Increments modCount //拿到插入位置后的elementData中元素的长度 int numMoved = size - index; //插入位置不是在elementData末尾 if (numMoved > 0) //复制,将插入位置后的元素,全部后挪到能够容纳插入新元素的位置 System.arraycopy(elementData, index, elementData, index + numNew, numMoved); //将插入的新元素复制进指定位置 System.arraycopy(a, 0, elementData, index, numNew); //增加数组长度 size += numNew; return numNew != 0; }
(6)移除单个元素
  #remove(int index) 方法,移除指定位置的元素,并返回该位置的原元素
public E remove(int index) { //检查插入位置是不是在数组内 rangeCheck(index); //操作数+1 modCount++; //拿到elementData中索引位置的元素 E oldValue = elementData(index); //拿到索引位置后元素的数量,这里注意size是从1开始的,index是从0开始的 int numMoved = size - index - 1;
if (numMoved > 0) //索引位置后的元素前挪 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
  #remove(Object o) 方法,移除首个为 o 的元素,并返回是否移除到
public boolean remove(Object o) { //如果删除元素是null if (o == null) { //循环遍历 for (int index = 0; index < size; index++) //如果等于null if (elementData[index] == null) { //删除 fastRemove(index); return true; } } else { //与上述同理 for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } private void fastRemove(int index) { //操作数+1 modCount++; //同删除单个元素,拿到索引位置后的元素数量 int numMoved = size - index - 1; if (numMoved > 0) //索引位置后元素前挪 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
(7)移除多个元素
  #removeRange(int fromIndex, int toIndex) 方法,批量移除 [fromIndex, toIndex) 的多个元素,注意不包括 toIndex 的元素
protected void removeRange(int fromIndex, int toIndex) { //操作数+1 modCount++; //toIndex后的元素数量,也就是需要移动的元素数量 int numMoved = size - toIndex; //toIndex后的元素前挪 System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // clear to let GC do its work //元素前挪后,将后边的位置置null int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; }
(8)批量移除指定的多个元素
  #removeAll(Collection<?> c) 方法,移除在c中的元素
public boolean removeAll(Collection<?> c) { //非空判断 Objects.requireNonNull(c); return batchRemove(c, false); } private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; //需要理解r和w的意思,r其实是遍历elementData的索引,w是elementData中的c有的元素,就是需要删除的元素的索引 int r = 0, w = 0; boolean modified = false; try { //循环遍历原数组 for (; r < size; r++) //如果elementData当前元素c中有,那么覆盖w位置的元素 if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. //异常处理 将r位置之后的的元素写入到w位置之后,保证不多出来 if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } //因为在之前就将需要剩下的元素前挪了,所以将w位置之后的元素置null if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }
  #retainAll(Collection<?> c) 求 elementData 数组和指定多个元素的交集,删除不在c中的元素,因为逻辑与removeRange相同,所以略过
(9)查找单个元素
  #indexOf(Object o) 方法,查找首个为指定元素的位置
public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
  #contains(Object o) 方法,就是基于该方法实现
public boolean contains(Object o) { return indexOf(o) >= 0; }
  有时我们需要查找最后一个为指定元素的位置,所以会使用到 #lastIndexOf(Object o) 方法
public int lastIndexOf(Object o) { //反向遍历 if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; }
(10)获取指定位置的元素
  #get(int index) 方法,获得指定位置的元素
public E get(int index) { //校验索引是否越界 rangeCheck(index); return elementData(index); } E elementData(int index) { //返回指定索引位置的元素 return (E) elementData[index]; }
(11)设置指定位置的元素
  #set(int index, E element) 方法,设置指定位置的元素
public E set(int index, E element) { //越界校验 rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
(12)转换成数组
  #toArray() 方法,将 ArrayList 转换成 [] 数组
public Object[] toArray() { //注意返回的是Object类型的数组 return Arrays.copyOf(elementData, size); }
  实际场景下,我们可能想要指定 T 泛型的数组,那么我们就需要使用到 #toArray(T[] a) 方法
public <T> T[] toArray(T[] a) { //如果a比elementData小,直接复制一个数组返回 if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); //如果elementData比a小,则将elementData复制到a中 System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
(13)判断相等
  #equals(Object o) 方法,判断是否相等
public boolean equals(Object o) { // 如果是自己,直接返回相等 if (o == this) { return true; } // 如果不为 List 类型,直接不相等 if (!(o instanceof List)) { return false; } // 获得当前的数组修改次数 final int expectedModCount = modCount; // ArrayList can be subclassed and given arbitrary behavior, but we can // still deal with the common case where o is ArrayList precisely // <X> 根据不同类型,调用不同比对的方法。主要考虑 ArrayList 可以直接使用其 elementData 属性,性能更优。 boolean equal = (o.getClass() == ArrayList.class) ? equalsArrayList((ArrayList<?>) o) : equalsRange((List<?>) o, 0, size); // 如果修改次数发生改变,则抛出 ConcurrentModificationException 异常 checkForComodification(expectedModCount); return equal; }
选择两种比较方法的原因:普通的list只能使用迭代器,相比与ArrayList的elementData遍历,性能会略低一些
boolean equalsRange(List<?> other, int from, int to) { // 如果 to 大于 es 大小,说明说明发生改变,抛出 ConcurrentModificationException 异常 final Object[] es = elementData; if (to > es.length) { throw new ConcurrentModificationException(); } // 通过迭代器遍历 other ,然后逐个元素对比 var oit = other.iterator(); for (; from < to; from++) { // 如果 oit 没有下一个,或者元素不相等,返回 false 不匹配 if (!oit.hasNext() || !Objects.equals(es[from], oit.next())) { return false; } } // 通过 oit 是否遍历完。实现大小是否相等的效果。 return !oit.hasNext(); } private boolean equalsArrayList(ArrayList<?> other) { // 获得 other 数组修改次数 final int otherModCount = other.modCount; final int s = size; boolean equal; // 判断数组大小是否相等 if (equal = (s == other.size)) { final Object[] otherEs = other.elementData; final Object[] es = elementData; // 如果 s 大于 es 或者 otherEs 的长度,说明发生改变,抛出 ConcurrentModificationException 异常 if (s > es.length || s > otherEs.length) { throw new ConcurrentModificationException(); } // 遍历,逐个比较每个元素是否相等 for (int i = 0; i < s; i++) { if (!Objects.equals(es[i], otherEs[i])) { equal = false; break; // 如果不相等,则 break } } } // 如果 other 修改次数发生改变,则抛出 ConcurrentModificationException 异常 other.checkForComodification(otherModCount); return equal; }
(14)清空数组
public void clear() { modCount++; // clear to let GC do its work //遍历置null for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
(15)克隆
  #clone()方法,克隆 ArrayList 对象
public Object clone() { try { //调用父类方法克隆 ArrayList<?> v = (ArrayList<?>) super.clone(); //复制到新数组 v.elementData = Arrays.copyOf(elementData, size); //操作数归零 v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }
(16)创建子数组
  #subList(int fromIndex, int toIndex) 方法,创建 ArrayList 的子数组
public List<E> subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); return new SubList(this, 0, fromIndex, toIndex); }
  代码并不复杂,需要注意的是,SubList 不是一个只读数组,而是和根数组 root 共享相同的 elementData 数组,只是说限制了 [fromIndex, toIndex) 的范围
(17)创建Iterator迭代器
  #iterator() 方法,创建迭代器。一般情况下,我们使用迭代器遍历 ArrayList、LinkedList 等等 List 的实现类
public Iterator<E> iterator() { return new Itr(); }
  创建 Itr 迭代器。Itr 实现 java.util.Iterator 接口,是 ArrayList 的内部类。虽然说 AbstractList 也提供了一个 Itr 的实现,但是 ArrayList 为了更好的性能,所以自己实现了,在其类上也有注释“An optimized version of AbstractList.Itr”
Itr一共有三个属性:
/** * 下一个访问元素的位置,从下标 0 开始。 */ int cursor; // index of next element to return /** * 上一次访问元素的位置。 * * 1. 初始化为 -1 ,表示无上一个访问的元素 * 2. 遍历到下一个元素时,lastRet 会指向当前元素,而 cursor 会指向下一个元素。这样,如果我们要实现 remove 方法,移除当前元素,就可以实现了。 * 3. 移除元素时,设置为 -1 ,表示最后访问的元素不存在了,都被移除咧。 */ int lastRet = -1; // index of last element returned; -1 if no such /** * 创建迭代器时,数组修改次数。 * * 在迭代过程中,如果数组发生了变化,会抛出 ConcurrentModificationException 异常。 */ int expectedModCount = modCount; // prevent creating a synthetic constructor Itr() {}
Itr对Iterator的四个实现方法:
  #hasNext() 方法,判断是否还可以继续迭代
public boolean hasNext() { //cursor = size,说明到头了 return cursor != size; }
  #next() 方法,下一个元素
public E next() { //检查操作数,判断数组是否发生了变化 checkForComodification(); //超出size,抛出异常 //i记录当前cursor位置 int i = cursor; if (i >= size) throw new NoSuchElementException(); //判断如果超出elementData大小,则说明被修改,抛出异常 Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); //cursor指向下一个位置 cursor = i + 1; //返回当前位置的元素,且lastRet指向当前位置 return (E) elementData[lastRet = i]; }
  #remove() 方法,移除当前元素
public void remove() { //lastRet小于0,则没有指向任何元素,抛出异常 if (lastRet < 0) throw new IllegalStateException(); //检查数组是否发生变化 checkForComodification(); try { //移除lastRet位置的元素 ArrayList.this.remove(lastRet); //cursor指向lastRet位置,因为被移除了,需要后退 cursor = lastRet; //因为元素被移除,则lastRet不指向任何位置 lastRet = -1; //记录新的数组修改数 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
  #forEachRemaining(Consumer<? super E> action) 方法,消费剩余未迭代的元素
public void forEachRemaining(Consumer<? super E> consumer) { //判断是否为null,为null抛出异常 Objects.requireNonNull(consumer); //获取当前数组大小 final int size = ArrayList.this.size; //将i指向cursor int i = cursor; //如果大于size,则没有需要消费的元素 if (i >= size) { return; } //拿到当前数组 final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } //逐个处理 while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic //更新cursor、lastRet的指向 cursor = i; lastRet = i - 1; checkForComodification(); }
 
                    
                 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号