Java 1.8 ArrayList源码解析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 1.8 ArrayList源码解析相关的知识,希望对你有一定的参考价值。

1 // 非线程安全
2 // 继承了AbstractList类
3 // 实现了List、RandomAccess、Cloneable、java.io.Serializable接口
4 // 后面3个接口是标记接口,没有抽象方法。
5 // 表示ArrayList可以随机访问、浅复制、序列化和反序列化。
6 public class ArrayList<E> extends AbstractList<E>
7         implements List<E>, RandomAccess, Cloneable, java.io.Serializable

 

1 // 序列化版本唯一标识符,版本向上兼容。
2 private static final long serialVersionUID = 8683452581122892189L;

 

1 // 默认初始容量
2 private static final int DEFAULT_CAPACITY = 10;

 

1 // 容量为0的空数组
2 private static final Object[] EMPTY_ELEMENTDATA = {};

 

1 // 默认容量的空数组,向空数组中插入第1个元素时该数组可用于判断初始的容量是多少(0或10)从而确定扩容的大小。
2 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

 

1 // 瞬态的缓冲数组 使用transient关键字目的是只序列化实际存储的那些元素,而不是整个数组。
2 transient Object[] elementData; // non-private to simplify nested class access

 

1 // 数组实际存储的元素数量
2 private int size;

 

 1 // 带初始容量的构造函数
 2 public ArrayList(int initialCapacity) {
 3     if (initialCapacity > 0) {
 4         this.elementData = new Object[initialCapacity];
 5     } else if (initialCapacity == 0) {
 6         // 指向空数组
 7         this.elementData = EMPTY_ELEMENTDATA;
 8     } else {
 9         throw new IllegalArgumentException("Illegal Capacity: "+
10                                            initialCapacity);
11     }
12 }

 

1 // 无参构造函数
2 public ArrayList() {
3     // 指向默认容量为10的空数组
4     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
5 }

 

 1 // Collection对象作为参数的构造函数
 2 public ArrayList(Collection<? extends E> c) {
 3     elementData = c.toArray();
 4     if ((size = elementData.length) != 0) {
 5         // c.toArray might (incorrectly) not return Object[] (see 6260652)
 6         // 返回的数组类型可能不是Object[],要看运行时类型(实际类型),例如String[]。这样会导致向下转型,例如修改元素时指向了非继承或实现关系的对象时会出错,String->Integer。
 7         // 原因在于里面存储的元素可能不是Object类型。
 8         if (elementData.getClass() != Object[].class)
 9             // 重新指向Object数组。
10             // Arrays.copyOf会创建新数组,返回引用。
11             elementData = Arrays.copyOf(elementData, size, Object[].class);
12     } else {
13         // replace with empty array.
14         this.elementData = EMPTY_ELEMENTDATA;
15     }
16 }

 

1 // 将数组长度削减到元素数量对应的长度。
2 public void trimToSize() {
3     modCount++; // 快速失败机制
4     if (size < elementData.length) { // 如果实际元素数量小于数组长度
5         elementData = (size == 0)
6           ? EMPTY_ELEMENTDATA
7           : Arrays.copyOf(elementData, size);
8     }
9 }

   

  fail-fast机制

  fail-fast机制也叫作”快速失败”机制,是Java集合中的一种错误检测机制。

  在对集合进行迭代过程中,除了迭代器可以对集合进行数据结构上进行修改,其他的对集合的数据结构进行修改,都会抛出ConcurrentModificationException错误。

  这里,所谓的进行数据结构上进行修改,是指对存储的对象,进行add、set、remove操作,进而对数据发生改变。

  ArrayList中,有个modCount的变量,每次进行add、set、remove等操作,都会执行modCount++。

  在获取ArrayList的迭代器时,会将ArrayList中的modCount保存在迭代中,每次执行add,set,remove等操作,都会执行一次检查,调用checkForComodification方法,对modCount进行比较。如果迭代器中的modCount和List中的modCount不同,则抛出ConcurrentModificationException。

 

 1 // 公有方法,保证容量。
 2 public void ensureCapacity(int minCapacity) {
 3     // 最小增量
 4     int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
 5         // any size if not default element table
 6         ? 0
 7         // larger than default for default empty table. It‘s already
 8         // supposed to be at default size.
 9         : DEFAULT_CAPACITY;
10     
11     // 如果最小容量大于最小增量则确保最小容量足够。
12     if (minCapacity > minExpand) {
13         ensureExplicitCapacity(minCapacity);
14     }
15 }

 

 1 // 确保内部的容量足够。
 2 private void ensureCapacityInternal(int minCapacity) {
 3     // 如果elementData指向的是默认容量的空数组则说明当前容量为10。
 4     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
 5         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
 6     }
 7     
 8     // 确保最小容量足够。
 9     ensureExplicitCapacity(minCapacity);
10 }

 

1 // 确保最小容量足够。
2 private void ensureExplicitCapacity(int minCapacity) {
3     modCount++; // 快速失败机制
4 
5     // overflow-conscious code
6     // 如果最小容量大于数组长度则扩容。
7     if (minCapacity - elementData.length > 0)
8         grow(minCapacity);
9 }

 

1 /**
2  * The maximum size of array to allocate.
3  * Some VMs reserve some header words in an array.
4  * Attempts to allocate larger arrays may result in
5  * OutOfMemoryError: Requested array size exceeds VM limit
6  */
7 // 可分配的数组最大容量
8 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

 

 1 // 扩容
 2 private void grow(int minCapacity) { // minCapacity是需要达到的最小容量。
 3     // overflow-conscious code
 4     // 原来的容量是数组长度。
 5     int oldCapacity = elementData.length;
 6     // 新的容量是1.5倍的原有容量。
 7     int newCapacity = oldCapacity + (oldCapacity >> 1); 
 8     
 9     // 如果最小容量大于1.5倍的原有容量则最小容量设为新的容量。
10     if (newCapacity - minCapacity < 0) 
11         newCapacity = minCapacity;
12     // 如果新的容量大于可分配的数组最大容量
13     if (newCapacity - MAX_ARRAY_SIZE > 0) 
14         // minCapacity is usually close to size, so this is a win:
15         newCapacity = hugeCapacity(minCapacity); 
16     
17     // elementData指向扩容后的新数组。
18     elementData = Arrays.copyOf(elementData, newCapacity);
19 }

 

 1 // 确保数组容量最大为Integer.MAX_VALUE。
 2 private static int hugeCapacity(int minCapacity) {
 3     // 如果最小容量溢出则抛出内存不足错误。
 4     if (minCapacity < 0) // overflow
 5         throw new OutOfMemoryError();
 6     
 7     // 如果最小容量超过了可分配的数组最大容量则返回整形的最大值作为新容量,否则返回可分配的数组最大容量作为新容量。
 8     return (minCapacity > MAX_ARRAY_SIZE) ?
 9         Integer.MAX_VALUE :
10         MAX_ARRAY_SIZE;
11 }

 

1 // 获取实际元素数量。
2 public int size() {
3     return size;
4 }

 

1 // 判断数组是否为空即判断是否存在实际元素。
2 public boolean isEmpty() {
3     return size == 0;
4 }

 

1 // 判断是否包含某个元素,通过该元素对应的下标来判断。
2 public boolean contains(Object o) {
3     return indexOf(o) >= 0;
4 }

 

 1 // 顺序获取某个元素对应的下标。
 2 public int indexOf(Object o) {
 3     if (o == null) { // 如果该元素为空
 4         for (int i = 0; i < size; i++)
 5             if (elementData[i] == null)
 6                 return i;
 7     } else { // 否则
 8         for (int i = 0; i < size; i++)
 9             if (o.equals(elementData[i]))
10                 return i;
11     }
12     
13     // 没有找到该元素。
14     return -1;
15 }

 

 1 // 逆序获取某个元素对应的下标。
 2 public int lastIndexOf(Object o) {
 3     if (o == null) { // 如果该元素为空
 4         for (int i = size-1; i >= 0; i--)
 5             if (elementData[i] == null)
 6                 return i;
 7     } else { // 否则
 8         for (int i = size-1; i >= 0; i--)
 9             if (o.equals(elementData[i]))
10                 return i;
11     }
12     
13     // 没有找到该元素。
14     return -1;
15 }

 

 1 // 浅复制,没有复制数组中元素作为引用指向的对象。该方法返回的是当前ArrayList对象的浅复制对象引用。
 2 public Object clone() {
 3     try {
 4         // 调用父类复制方法。
 5         ArrayList<?> v = (ArrayList<?>) super.clone();
 6         // v.elementData指向存储原来对象(元素)的新数组。
 7         v.elementData = Arrays.copyOf(elementData, size); 
 8         v.modCount = 0; // 快速失败机制
 9         
10         return v; // 返回引用。
11     } catch (CloneNotSupportedException e) {
12         // this shouldn‘t happen, since we are Cloneable
13         throw new InternalError(e);
14     }
15 }

 

1 // 返回一个数据拷贝后的Object数组。
2 public Object[] toArray() {
3     return Arrays.copyOf(elementData, size);
4 }

 

 1 // 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
 2 @SuppressWarnings("unchecked")
 3 public <T> T[] toArray(T[] a) {
 4     if (a.length < size)
 5         // Make a new array of a‘s runtime type, but my contents:
 6         return (T[]) Arrays.copyOf(elementData, size, a.getClass());
 7     
 8     System.arraycopy(elementData, 0, a, 0, size);
 9     
10     if (a.length > size)
11         a[size] = null;
12     
13     return a;
14 }

 

1 // 按下标随机访问元素。
2 @SuppressWarnings("unchecked")
3 E elementData(int index) {
4     return (E) elementData[index];
5 }

 

1 // 通过参数检查之后按下标随机访问元素。
2 public E get(int index) {
3     rangeCheck(index);
4 
5     return elementData(index);
6 }

 

1 // 通过参数检查之后按下标修改元素内容并返回原来的元素内容。
2 public E set(int index, E element) {
3     rangeCheck(index);
4 
5     E oldValue = elementData(index);
6     elementData[index] = element;
7     
8     return oldValue;
9 }

 

1 // 末尾增加元素。
2 public boolean add(E e) {
3     // 确保容量足够。
4     ensureCapacityInternal(size + 1);  // Increments modCount!!
5     elementData[size++] = e;
6     
7     return true;
8 }

 

 1 // 按下标增加元素。可能需要向后移动。
 2 public void add(int index, E element) {
 3     // 下标检查。
 4     rangeCheckForAdd(index);
 5     
 6     // 确保容量足够。
 7     ensureCapacityInternal(size + 1);  // Increments modCount!!
 8     // 下标大于index的部分向后移。注意size>=index。
 9     System.arraycopy(elementData, index, elementData, index + 1,
10                      size - index);
11     elementData[index] = element; // 插入新元素。
12     size++; // 实际元素数量增加。
13 }

 

 1 // 按下标删除元素并返回原来的元素内容。
 2 public E remove(int index) {
 3     // 下标检查。
 4     rangeCheck(index);
 5 
 6     modCount++; // 快速失败机制
 7     E oldValue = elementData(index);
 8     
 9     // 可能需要向前移动。注意size>=index。所以numMoved可能小于0。
10     int numMoved = size - index - 1;
11     if (numMoved > 0)
12         System.arraycopy(elementData, index+1, elementData, index,
13                          numMoved);
14     elementData[--size] = null; // clear to let GC do its work
15 
16     return oldValue;
17 }

 

 1 // 移除此列表中首次出现的指定元素(如果存在)。
 2 public boolean remove(Object o) {
 3     if (o == null) { // 如果是空元素
 4         for (int index = 0; index < size; index++)
 5             if (elementData[index] == null) {
 6                 fastRemove(index); // 不返回旧值。
 7                 return true;
 8             }
 9     } else { // 否则
10         for (int index = 0; index < size; index++)
11             if (o.equals(elementData[index])) {
12                 fastRemove(index); // 不返回旧值。
13                 return true;
14             }
15     }
16     
17     // 没有找到该元素。
18     return false;
19 }

 

 1 // 根据下标删除元素,但不返回旧值。
 2 private void fastRemove(int index) {
 3     modCount++; // 快速失败机制
 4     
 5     // 可能需要向前移动。注意size>=index。所以numMoved可能小于0。
 6     int numMoved = size - index - 1;
 7     if (numMoved > 0)
 8         System.arraycopy(elementData, index+1, elementData, index,
 9                          numMoved);
10     elementData[--size] = null; // clear to let GC do its work
11 }

 

 1 // 移除此列表中的所有元素。
 2 public void clear() {
 3     modCount++; // 快速失败机制
 4 
 5     // clear to let GC do its work
 6     for (int i = 0; i < size; i++)
 7         elementData[i] = null;
 8 
 9     size = 0;
10 }

 

 1 // 将指定collection中的所有元素添加到此列表的尾部。
 2 public boolean addAll(Collection<? extends E> c) {
 3     // 得到c对应的Object[]数组。
 4     Object[] a = c.toArray();
 5     // 获取新增的元素数量。
 6     int numNew = a.length;
 7     // 确保容量足够。
 8     ensureCapacityInternal(size + numNew);  // Increments modCount
 9     // 将新的元素添加在尾部。elementData指向的数组里面存储的都是Object类型元素。
10     System.arraycopy(a, 0, elementData, size, numNew);
11     // 增加实际元素数量。
12     size += numNew;
13     
14     return numNew != 0;
15 }

 

 1 // 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。
 2 public boolean addAll(int index, Collection<? extends E> c) {
 3     // 下标检查。
 4     rangeCheckForAdd(index);
 5     
 6     // 得到c对应的Object[]数组。
 7     Object[] a = c.toArray();
 8     // 获取新增的元素数量。
 9     int numNew = a.length;
10     // 确保容量足够。
11     ensureCapacityInternal(size + numNew);  // Increments modCount
12     
13     // 可能需要向前移动。注意size>=index。所以numMoved可能小于0。
14     int numMoved = size - index;
15     if (numMoved > 0)
16         System.arraycopy(elementData, index, elementData, index + numNew,
17                          numMoved);
18     
19     // 从index开始添加新元素。
20     System.arraycopy(a, 0, elementData, index, numNew);
21     // 增加实际元素数量。
22     size += numNew;
23     
24     return numNew != 0;
25 }

 

 1 // 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
 2 protected void removeRange(int fromIndex, int toIndex) {
 3     modCount++; // 快速失败机制
 4     // 可能需要向前移动。
 5     int numMoved = size - toIndex; // 元素移动数量
 6     System.arraycopy(elementData, toIndex, elementData, fromIndex,
 7                      numMoved);
 8 
 9     // clear to let GC do its work
10     // newSize是新的实际元素数量。
11     int newSize = size - (toIndex-fromIndex);
12     for (int i = newSize; i < size; i++) {
13         elementData[i] = null;
14     }
15     size = newSize; // 更新实际元素数量。
16 }

 

1 // 下标上界范围检查。
2 private void rangeCheck(int index) {
3     if (index >= size)
4         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
5 }

 

1 // 增加元素时下标范围检查。
2 private void rangeCheckForAdd(int index) {
3     if (index > size || index < 0)
4         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
5 }

 

1 // 输出越界异常提示。
2 private String outOfBoundsMsg(int index) {
3     return "Index: "+index+", Size: "+size;
4 }

 

 1 // 序列化,elementData写入输出流。
 2 private void writeObject(java.io.ObjectOutputStream s)
 3     throws java.io.IOException{
 4     // Write out element count, and any hidden stuff
 5     int expectedModCount = modCount;
 6     // 版本向上兼容,旧版本的反序列化值在新版本中序列化会增加新属性的默认值。
 7     s.defaultWriteObject(); 
 8 
 9     // Write out size as capacity for behavioural compatibility with clone()
10     // 版本向上兼容,旧版本需要读取数组长度length。如果不写入size,会导致旧版本反序列化时出现异常。
11     s.writeInt(size);
12 
13     // Write out all elements in the proper order.
14     for (int i=0; i<size; i++) {
15         s.writeObject(elementData[i]);
16     }
17     
18     // 快速失败机制
19     if (modCount != expectedModCount) {
20         throw new ConcurrentModificationException();
21     }
22 }

   

 1 // 反序列化,elementData从输入流中读取。
 2 private void readObject(java.io.ObjectInputStream s)
 3     throws java.io.IOException, ClassNotFoundException {
 4     // elementData指向空数组。
 5     elementData = EMPTY_ELEMENTDATA;
 6     
 7     // Read in size, and any hidden stuff
 8     // 版本向上兼容,旧版本的序列化值在新版本中反序列化时会自动忽略新属性。
 9     s.defaultReadObject(); 
10 
11     // Read in capacity
12     s.readInt(); // ignored
13     
14     // size是通过ObjectOutputStream类中defaultWriteFields方法和ObjectInputStream类中defaultReadFields方法得到的。
15     // 如果存在元素
16     if (size > 0) { 
17         // be like clone(), allocate array based upon size not capacity
18         // 确保容量足够。
19         ensureCapacityInternal(size);
20         
21         Object[] a = elementData;
22         // Read in all elements in the proper order.
23         for (int i=0; i<size; i++) {
24             a[i] = s.readObject();
25         }
26     }
27 }

 

  ObjectOutputStream会调用ArrayList类的writeObject方法进行序列化,ObjectInputStream会调用相应的readObject方法进行反序列化。通过反射机制,ObjectOutputStream的writeObject会根据传进来的ArrayList对象得到Class,然后再包装成ObjectStreamClass,在writeSerialData方法里,会调用ObjectStreamClass的 invokeWriteObject方法,从而调用ArrayList类中的writeObject方法。

 

 1 // 排序
 2 @Override
 3 @SuppressWarnings("unchecked")
 4 public void sort(Comparator<? super E> c) {
 5     final int expectedModCount = modCount;
 6     Arrays.sort((E[]) elementData, 0, size, c);
 7     // 快速失败机制
 8     if (modCount != expectedModCount) {
 9         throw new ConcurrentModificationException();
10     }
11     modCount++;
12 }

 

  参考资料

  ArrayList中elementData为什么被transient修饰?

  Java笔记---c.toArray() might (incorrectly) not return object[] (see 6260652)官方Bug

  java ArrayList的序列化分析

  ArrayList中size为什么要序列化两次?

  ArrayList概念及手写代码

  Java集合---ArrayList的实现原理

  java8 ArrayList源码阅读

  jdk1.8.0_45源码解读——ArrayList的实现

  java.util.List.toArray() 使用体会

以上是关于Java 1.8 ArrayList源码解析的主要内容,如果未能解决你的问题,请参考以下文章

jdk 1.8下 java ArrayList 添加元素解析

ArrayList源码解析入门篇

Java集合之ArrayList源码解析

Java 集合深入理解 :ArrayList源码解析,及动态扩容机制

LinkedList 源码分析(JDK 1.8)

Java深入研究9HashMap源码解析(jdk 1.8)