本文基于 JDK8
ArrayList 底层通过动态数组的数据结构实现
ArrayList 的继承与实现的关系图如下所示。
以下说明摘自 JDK 文档。
/** * 序列号 */ private static final long serialVersionUID = 8683452581122892189L; /** * 默认容量为 10,通过 new ArrayList() 创建 */ private static final int DEFAULT_CAPACITY = 10; /** * 空数组,传入容量为 0 时使用,通过 new ArrayList(0) 创建 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 空数组,与 EMPTY_ELEMENTDATA 区分开来,通过 new ArrayList() 创建 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 存储元素的数组 * transient 修饰此对象表示不序列化该属性,因为 ArrayList 具有动态扩容的特性,数组中的元素会有剩余 * 通过 writeObject 和 readObject 方法实现序列化和反序列化 */ transient Object[] elementData; // non-private to simplify nested class access /** * 数组的实际长度 */ private int size; /** * 可以分配的最大容量 * Integer.MAX_VALUE - 8 是因为数组中有虚拟机保留的一些数据 * 强制分配可能会导致 OutOfMemoryError */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;复制代码
无参构造器,通过 new ArrayList() 调用。
/** * 无参构造器,通过 new ArrayList() 调用 * 懒初始化,在添加第一个元素时将 elementData 扩容为 DEFAULT_CAPACITY,减少内存的开销 */ public ArrayList() { // 将 elementData 初始化为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }复制代码
构造一个具有指定初始容量的集合。
/** * 构造一个具有指定初始容量的集合 * @param initialCapacity 初始容量 * @throws IllegalArgumentException 初始容量为负时抛出异常 */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { // 初始化容量大于 0 的话,构造一个容量为 initialCapacity 的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { // 初始化容量等于 0 的话,初始化为空数组 EMPTY_ELEMENTDATA this.elementData = EMPTY_ELEMENTDATA; } else { // 初始化容量小于 0 的话就抛出异常 throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity); } }复制代码
构造一个包含指定集合元素的集合。
/** * 构造一个包含指定集合元素的集合 * @param c 将其元素放入此列表的集合 * @throws NullPointerException 集合为空时抛出 */ public ArrayList(Collection<? extends E> c) { // 将集合转为 Object[] 类型数组 elementData = c.toArray(); // 将 elementData.length 赋值给 size 并判断是否为 0 if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) // 因为当子类重写父类时,可以修改返回值类型,导致返回的类型可能不为 Object[] if (elementData.getClass() != Object[].class) // 利用 Arrays 的 copyOf 函数复制成 Object[] 类型的 elementData elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // 传入集合长度为 0 的话,初始化为空数组 EMPTY_ELEMENTDATA this.elementData = EMPTY_ELEMENTDATA; } }复制代码
添加特定的元素到集合末尾。
/** * 添添加特定的元素到集合末尾 * @param e 添加到集合的元素 * @return 返回是否插入成功 */ public boolean add(E e) { // 检查插入一个元素是否需要扩容 ensureCapacityInternal(size + 1); // 将元素插入到最后一位 elementData[size++] = e; return true; } /** * 确保传入的最小内部容量 * @param minCapacity 最小容量 */ private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } /** * 计算所需要的最小容量 * @param elementData 原始数组 * @param minCapacity 所需要的最小容量 * @return 扩容后的大小 */ private static int calculateCapacity(Object[] elementData, int minCapacity) { // 若 elementData 为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,也就是通过 new ArrayList() 初始化的 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 此时 minCapacity 应该为 1,返回初始化的默认容量 10 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } /** * 确保明确的需要扩容 * @param minCapacity 所需要的最小容量 */ private void ensureExplicitCapacity(int minCapacity) { // 修改次数 +1,用于 fail-fast 机制 modCount++; // 防止代码溢出,当所需要的最小容量大于 elementData 的长度时才进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * 增加容量以确保它至少可以保存最小容量参数指定数量的元素。 * @param minCapacity 所需要的最小容量 */ private void grow(int minCapacity) { // 旧容量大小 int oldCapacity = elementData.length; // 新容量 = 旧容量 + 旧容量*0.5,也就是旧容量的 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: // 扩容完毕,win!将新容量复制给 elementData elementData = Arrays.copyOf(elementData, newCapacity); } /** * 因为根据旧容量*1.5分配容量超出最大容量,所以该函数用于计算最大容量分配 * @param minCapacity * @return */ private static int hugeCapacity(int minCapacity) { // 所需要最小容量小于 0 抛出 OutOfMemoryError if (minCapacity < 0) throw new OutOfMemoryError(); // 当所需容量大于 MAX_ARRAY_SIZE 时返回 Integer.MAX_VALUE 否则返回 MAX_ARRAY_SIZE return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }复制代码
将指定的元素插入集合中指定位置。将当前在该位置的元素(如果有的话)和所有后续元素向右移动。
/** * 将指定的元素插入集合中指定位置 * 将当前在该位置的元素(如果有的话)和任何后续元素向右移动 * @param index 插入元素的位置 * @param element 插入的元素 * @throws IndexOutOfBoundsException 数组越界异常 */ public void add(int index, E element) { // 检查该数组下标是否越界 rangeCheckForAdd(index); // 检查插入一个元素是否需要扩容 ensureCapacityInternal(size + 1); // 将 elementData 中 index 位置开始的元素 // 复制到 elementData 中 index + 1 开始的位置,复制长度为 size-index System.arraycopy(elementData, index, elementData, index + 1, size - index); // 将该元素添加到指定下标位置 elementData[index] = element; // 数组实际长度 +1 size++; } /** * 检查添加时数组下标是否越界 * add 和 addAll 使用的 rangeCheck 版本。 */ private void rangeCheckForAdd(int index) { // 下标大于数组实际长度或小于 0 时抛出 IndexOutOfBoundsException if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * 构造 IndexOutOfBoundsException 的详细异常信息 */ private String outOfBoundsMsg(int index) { // 返回越界的下标大小和实际数组长度 return "Index: "+index+", Size: "+size; }复制代码
将指定集合中的所有元素追加到集合的末尾。
/** * 将指定集合中的所有元素追加到集合的末尾 * * @param c 包含要添加到该集合的元素集合 * @return 集合是否插入成功 * @throws NullPointerException 指定集合为空时抛出 */ public boolean addAll(Collection<? extends E> c) { // 将集合 c 转为 Object[] 类型数组 Object[] a = c.toArray(); // 插入的集合的长度 int numNew = a.length; // 检查容量为 size + numNew 时的扩容 ensureCapacityInternal(size + numNew); // 将集合 a 中的所有元素拷贝到 elementData 的最后 System.arraycopy(a, 0, elementData, size, numNew); // 增加数组的实际长度 size += numNew; // 如果插入的集合不为空就返回 true,否则返回 false return numNew != 0; } 复制代码
从指定位置开始,将指定集合中的所有元素插入此集合。将当前位于该位置的元素(如果有)和所有后续元素右移。
/** * 从指定位置开始,将指定集合中的所有元素插入此集合 * 将当前位于该位置的元素(如果有)和所有后续元素右移) * * @param index 插入集合的指定索引位置 * @param c 插入的指定集合 * @return 集合是否插入成功 * @throws IndexOutOfBoundsException 插入的下标是否越界 * @throws NullPointerException 集合为空抛出异常 */ public boolean addAll(int index, Collection<? extends E> c) { // 检查插入的指定下标是否越界 rangeCheckForAdd(index); // 将集合 c 转为 Object[] 类型数组 Object[] a = c.toArray(); // 集合 c 的长度 int numNew = a.length; // 检查容量为 size + numNew 时的扩容 ensureCapacityInternal(size + numNew); // 指定位置后数组要移动的长度 int numMoved = size - index; if (numMoved > 0) // 将 elementData 中 index 开始的元素复制到 elementData 中 index+numNew 的位置,复制长度为 numMoved System.arraycopy(elementData, index, elementData, index + numNew, numMoved); // 将添加的数组 a 的数据复制到 elementData 中 index 开始的位置,复制长度为 numNew System.arraycopy(a, 0, elementData, index, numNew); // 增加数组的实际长度 size += numNew; // 如果插入的集合不为空就返回 true,否则返回 false return numNew != 0; }复制代码
删除集合中指定位置的元素,将所有后续元素向左移动。
/** * 删除集合中指定位置的元素,将所有后续元素向左移动。 * * @param index 删除的指定下标 * @return 返回删除的元素 * @throws IndexOutOfBoundsException 删除的下标越界时抛出 */ public E remove(int index) { // 检查下标是否越界 rangeCheck(index); // 修改次数 +1 modCount++; // 取出对应下标中要删除的元素 E oldValue = elementData(index); // 删除该下标中的元素,后面元素需要移动的长度 int numMoved = size - index - 1; if (numMoved > 0) // 如果 numMoved>0,也就是 index 不为最后一位 // 将 elementData 中 index+1 位置开始的元素复制到 index 开始的位置,复制长度为 numMoved System.arraycopy(elementData, index+1, elementData, index, numMoved); // 将最后一个元素置为 null,方便 GC 回收,删除的时候并没有缩小容量 elementData[--size] = null; // 返回删除的元素 return oldValue; } /** * 检查给定的索引是否在范围内。如果不是,则抛出 IndexOutOfBoundsException * 此方法不检查索引是否为负数,如果索引为负数,则抛出 ArrayIndexOutOfBoundsException */ private void rangeCheck(int index) { // 当该下标大于等于数组实际长度时抛出异常 if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }复制代码
从集合删除第一次出现的指定元素,如果存在,则将其删除。如果集合不包含元素,则集合保持不变。
/** * 从集合删除第一次出现的指定元素,如果存在,则将其删除。如果集合不包含元素,则集合保持不变。 * @param o 需要删除的元素 * @return 返回是否删除成功 */ public boolean remove(Object o) { // 如果要删除的元素为 null if (o == null) { // null 特殊处理,用 == 作比较 // 遍历所有 elementData,找到第一个值为 null 的,快速删除对应的下标,返回 true for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { // 遍历所有 elementData,使用 equals 比较两个对象,找出第一次相等位置的下标快速删除,返回 true for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } // 没有对应的元素返回 false return false; } /** * 私有remove 方法,跳过边界检查并且不返回删除的值。 * 与 remove(int index) 相比,缺少越界检查和返回值 */ private void fastRemove(int index) { // 减少越界检查,提高性能 // 修改次数 +1 modCount++; // 删除该下标中的元素,后面元素需要移动的长度 int numMoved = size - index - 1; if (numMoved > 0) // 如果 numMoved>0,也就是 index 不为最后一位 // 将 elementData 中 index+1 位置开始的元素复制到 index 开始的位置,复制长度为 numMoved System.arraycopy(elementData, index+1, elementData, index, numMoved); // 将最后一个元素置为 null,方便 GC 回收,删除的时候并没有缩小容量 elementData[--size] = null; // 无返回值 }复制代码
从集合中删除指定的集合中包含的所有元素。
/** * 从集合中删除指定的集合中包含的所有元素 * @param c 删除的集合 * @return 返回是否删除 * @throws ClassCastException 如果该集合的元素的类与指定的集合不兼容抛出 * @throws NullPointerException 如果此列表包含 null 元素,并且指定的集合不允许使用null元素抛出 */ public boolean removeAll(Collection<?> c) { // 集合 c 不为空,有空的话抛出 NullPointerException Objects.requireNonNull(c); // 批量删除包含集合 c 的元素 return batchRemove(c, false); } /** * 批量删除集合 * @param c 删除的集合 * @param complement 删除的方式 * complement == true 表示删除不包含在集合 c 中的元素,求两个集合的交集 * complement == false 表示删除包含在集合 c 中的元素,求两个集合的差集 * @return */ private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; // r 为写指针 w 为读指针 int r = 0, w = 0; // 是否修改的标志 boolean modified = false; try { for (; r < size; r++) // 当 c 中不包含 elementData[r] 且 complement == false 时,elementData[w++] 存放的保留元素就是 elementData 中除去集合 c 有的元素 // 当 c 中包含 elementData[r] 且 complement == true 时,elementData[w++] 存放的是保留元素就是 elementData 和 集合 c 的交集 // 不管哪种方式,存放的都是对应保留的元素 if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // 正常循环结束 r == size,否则 c.contains() 抛出了异常 if (r != size) { // 将 elementData 中 r 位置开始的元素复制到 elementData 中 w 开始的位置,复制长度为 size-r // 也就是将出错位置 r 开始所有元素移动到已经保留的元素 w 位置之后 System.arraycopy(elementData, r, elementData, w, size - r); // 更新保留的元素个数 w += size - r; } // 如果 w == size,表示全部元素保留,没有修改,返回 false if (w != size) { // 将未保留的元素置为 null,方便 GC 回收 for (int i = w; i < size; i++) elementData[i] = null; // 修改的次数 +未保留的元素个数 modCount += size - w; // 将保留的元素个数更新为数组实际长度 size = w; // 标记修改成功 modified = true; } } // 修改失败返回 false return modified; }复制代码
仅保留此集合中指定集合中包含的元素。换句话说,从集合中删除所有不包含在指定集合中的元素。
/** * 仅保留此集合中指定集合中包含的元素。换句话说,从集合中删除所有不包含在指定集合中的元素。 * @param c 需要保留的指定集合 * @return 返回是否删除 * @throws ClassCastException 如果该集合的元素的类与指定的集合不兼容抛出 * @throws NullPointerException 如果此列表包含 null 元素,并且指定的集合不允许使用null元素抛出 */ public boolean retainAll(Collection<?> c) { // 集合 c 不为空,有空的话抛出 NullPointerException Objects.requireNonNull(c); // 批量删除包含集合 c 的元素,和 removeAll 函数的区别就是 complement 参数 return batchRemove(c, true); }复制代码
返回集合中指定位置的元素。
/** * 返回集合中指定位置的元素 * @param index 需要返回元素的下标 * @return 返回指定位置的元素 * @throws IndexOutOfBoundsException 下标越界时抛出 */ public E get(int index) { // 检查下标是否越界 rangeCheck(index); // 返回对应索引的元素 return elementData(index); } /** * 返回对应位置的元素 */ @SuppressWarnings("unchecked") E elementData(int index) { // 取出对应位置的元素并强转 return (E) ele/mentData[index]; }复制代码
用指定的元素替换集合中指定位置的元素。
/** * 用指定的元素替换集合中指定位置的元素 * @param 替换的位置 * @param element 替换成的元素 * @return 返回被替换前的元素 * @throws IndexOutOfBoundsException 索引下标越界抛出 */ public E set(int index, E element) { // 检查下标是否越界 rangeCheck(index); // 取出对应下标的元素 E oldValue = elementData(index); // 更新指定位置的元素 elementData[index] = element; // 返回更新前的值 return oldValue; }复制代码
将该集合的容量调整为实际的大小,可以使用此操作来最大程度地减少元素的存储。
/** * 将该集合 elementData 的容量调整为实际的大小,可以使用此操作来最大程度地减少元素的存储。 */ public void trimToSize() { // 修改次数 +1 modCount++; // 如果集合实际长度小于 elementData 的长度 if (size < elementData.length) { // 如果实际长度为 0,elementData 初始化为 EMPTY_ELEMENTDATA,否则将 elementData 重新复制为一个长度为 size 的数组 elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }复制代码
实现 ArrayList 实例的序列化。
/** * 将 ArrayList 实例的状态保存到流中(即对其进行序列化)。 * @serialData 写出支持 ArrayList 实例的数组的长度(int),然后以正确的顺序写出所有元素(Object)。 */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // 定义 expectedModCount 记录写出前的 modCount, 防止在序列化期间元素被修改 int expectedModCount = modCount; // 默认的序列化方法(写入非 transient 和非 static 修饰的属性,size 属性会被写入) s.defaultWriteObject(); // 写出大小 size s.writeInt(size); // 按正确的顺序写出所有元素 for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } // 如果在此期间元素个数发生了变化,抛出 ConcurrentModificationException if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }复制代码
从反序列化中重构 ArrayList 实例。
/** * 从流中重构 ArrayList 实例(即反序列化)。 */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // 初始化空数组 elementData = EMPTY_ELEMENTDATA; // 默认的反序列化方法(读入非 transient 和非 static 修饰属性,size 属性会被读入) s.defaultReadObject(); // 读入集合的长度,可以忽略,只是为了和写出对应 s.readInt(); if (size > 0) { // 计算所需要的最小容量 int capacity = calculateCapacity(elementData, size); // 检查集合的容量和类型 SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); // 根据 size 的大小进行扩容检查 ensureCapacityInternal(size); // 定义空数组 Object[] a = elementData; // 按顺序读取元素存入数组 for (int i=0; i<size; i++) { a[i] = s.readObject(); } } } 复制代码
删除集合中所有元素。
/** * 删除集合中所有元素 */ public void clear() { // 修改次数 +1 modCount++; // 给每个元素赋为 null,以便让 GC 回收 for (int i = 0; i < size; i++) elementData[i] = null; // 数组实际长度置为 0 size = 0; } 复制代码
以正确的顺序(从第一个元素到最后一个元素)返回一个包含此集合中所有元素的数组。
/** * 以正确的顺序(从第一个元素到最后一个元素)返回一个包含此集合中所有元素的数组。 * 此方法充当基于数组的 API 和基于集合的 API 之间的桥梁。 * @return 返回以适当顺序包含此列表中所有元素的数组 */ public Object[] toArray() { // 将集合复制为数组 return Arrays.copyOf(elementData, size); } 复制代码
判断集合是否包含指定元素。
/* * 判断集合是否包含指定元素。 */ public boolean contains(Object o) { // 通过调用 indexOf 方法返回的下标来判断是否存在该元素 return indexOf(o) >= 0; } /** * 返回指定元素在集合中首次出现的索引,如果集合不包含该元素,则返回-1 */ public int indexOf(Object o) { // 如果指定元素为 null if (o == null) { // 从头遍历所有元素,null 用 == 判断,返回第一次 null 出现的下标 for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { // 从头遍历所有元素,使用 equals 判断,返回第一次该元素出现的下标 for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } // 不存在该元素则返回 -1 return -1; } 复制代码
返回指定元素在集合中最后一次出现的索引,如果集合不包含该元素,则返回 -1。
/** * 返回指定元素在集合中最后一次出现的索引,如果集合不包含该元素,则返回-1 */ public int lastIndexOf(Object o) { // 如果指定元素为 null if (o == null) { // 从后往前遍历所有元素,null 用 == 判断,返回第一次 null 出现的下标,也就是最后一次出现 for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { // 从后往前遍历所有元素,使用 equals 判断,返回第一次该元素出现的下标,也就是最后一次出现 for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } // 不存在该元素则返回 -1 return -1; } 复制代码
返回集合中的元素的个数。
/** * 返回集合中的元素的个数 */ public int size() { // 直接返回 size return size; } 复制代码
判断集合是否为空。
/** * 判断集合是否为空 */ public boolean isEmpty() { // 通过判断集合实际长度是否为 0 return size == 0; } 复制代码
返回集合中指定的 [fromIndex, toIndex) 位置之间的集合。 如果 fromIndex == toIndex,则返回集合为空。
/** * 返回集合中指定 [fromIndex, toIndex) 位置元素构成的集合 * 如果 fromIndex == toIndex,返回空集合 */ public List<E> subList(int fromIndex, int toIndex) { // 检测子集的下标是否越界 subListRangeCheck(fromIndex, toIndex, size); // 通过构造 SubList 返回,SubList 和 ArrayList 引用是同一个对象 return new SubList(this, 0, fromIndex, toIndex); } /* * 检测子集的下标是否越界 */ static void subListRangeCheck(int fromIndex, int toIndex, int size) { // 检测 fromIndex 是否小于 0 if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); // 检测 toIndex 是否大于 size if (toIndex > size) throw new IndexOutOfBoundsException("toIndex = " + toIndex); // 检测 fromIndex 是否大于 toIndex if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } 复制代码
从集合中的指定位置开始,以适当的顺序返回此集合中元素的 list 迭代器。 指定的索引表示首次调用 ListIterator 的 next 将返回的第一个元素。 首次调用 ListIterator 的 previous 将返回具有指定索引减一的元素。ListIterator 迭代器为 List 特有的迭代器。
/** * 从集合中的指定位置开始,以适当的顺序返回此集合中元素的 list 迭代器。 * 返回的 list 迭代器为 fast-fail * @throws IndexOutOfBoundsException */ public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); // 构建一个从指定索引位置的迭代器 return new ListItr(index); } 复制代码
以正确的顺序返回此集合中的所有元素的 list 迭代器。
/** * 返回此集合中的所有元素的 list 迭代器 * 返回的 list 迭代器为 fast-fail */ public ListIterator<E> listIterator() { // 构建一个从头开始的 list 迭代器 return new ListItr(0); } 复制代码
以正确的顺序返回此集合中的所有元素的迭代器。
/** * 以正确的顺序返回此集合中的所有元素的迭代器 * 返回的迭代器为 fast-fail */ public Iterator<E> iterator() { return new Itr(); } 复制代码
Itr 类实现了 Iterator 接口,具有迭代器的基本方法。
private class Itr implements Iterator<E> { /** * 下一个要返回的元素的索引 */ int cursor; /** * 返回最后一个元素的索引,没有则返回 -1 */ int lastRet = -1; /** * 构建迭代器对象时记录 modCount */ int expectedModCount = modCount; Itr() {} /** * 判断是否有下一个元素 */ public boolean hasNext() { return cursor != size; } /** * 获取下一个元素 */ @SuppressWarnings("unchecked") public E next() { checkForComodification(); /* 省略 */ } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { /* 省略 */ checkForComodification(); } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; // 将删除后的 modCount 赋给 expectedModCount expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } /** * 检测 ArrayList 中的 modCount 和当前迭代器对象的 expectedModCount 是否一致 */ final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } 复制代码
ListItr 类继承 Itr 类,实现 ListIterator 接口,在迭代器的基本方法上,扩充了 List 特有的迭代器方法。
private class ListItr extends Itr implements ListIterator<E> { /** * 初始化 ListItr */ ListItr(int index) { super(); cursor = index; } /** * 判断是否有上一个元素 */ public boolean hasPrevious() { return cursor != 0; } /** * 返回下一个元素的下标 */ public int nextIndex() { return cursor; } /** * 返回上一个元素的下标 */ public int previousIndex() { return cursor - 1; } /** * 返回上一个元素 */ @SuppressWarnings("unchecked") public E previous() { checkForComodification(); /* 省略 */ } /** * 修改元素 */ public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); /* 省略 */ } /** * 添加元素 */ public void add(E e) { checkForComodification(); try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; // 将添加后的 modCount 赋给 expectedModCount expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } } 复制代码
fail-fast 快速失败机制是集合类应对并发访问在对集合进行迭代过程中,内部对象结构发生变化的一种防护措施。
ArrayList 类的 iterator() 和 listIterator() 方法返回的迭代器都是 fail-fast 的。 如果列表在创建迭代器之后的任何时间进行结构上的修改,除非通过迭代器自己的 remove() 或 add(Object) 方法,否则迭代器将抛出 ConcurrentModificationException。因此,面对 并发修改,迭代器会迅速而干净地失败,而不是 在将来的不确定时间内冒着不确定行为的风险。
在迭代过程执行 Iterator 的 remove 或 next 方法时,会通过 checkForComodification 方法来判断 modCount 是否发生了变化,如果在迭代过程中执行了 ArrayList 方法的 remove 或 add 等方法会造成 modeCount 改变,此时通过 checkForComodification 方法判断发现 expectedModCount != modCount,则抛出 ConcurrentModificationException。因此在迭代过程中进行删除操作时,需要调用 Iterator 的 remove 方法,另外 foreach 循环本质上也是迭代器实现的。
文章同步到公众号和 Github ,有问题的话可以联系作者。