ArrayList源码分析 Hey 大家好,我是 Shio👋。今天我们将深入探讨 Java 的ArrayList源码, ArrayList 是Java提供的动态数组容器, 和Java中数组相比, 它能够动态增加长度。
其继承了AbstractList, 并且实现了List, RandomAccess, Cloneable, Serializable接口
public  class  ArrayList <E> extends  AbstractList <E>        implements  List <E>, RandomAccess, Cloneable, java.io.Serializable{   } 
 
这些接口代表的含义如下:
List代表他是一个列表, 支持添加,删除, 查找操作, 可存放重复元素 
RandomAccess 是一个标记接口, 代表实现类支持随机访问(根据元素序号快速获取元素对象) 
Cloneable是一个标记接口, 代表实现类具有拷贝能力(可以调用Object.clone()方法进行拷贝) 
Serializable是一个标记接口, 代表实现类可以进行序列化操作(可以转化为字节流进行持久化或者网络传输),同时其ArrayList也实现了readObject() 和writeObject()方法自定义序列化(后面会讲) 
 
源码解析 扩容机制 我们先从最高频的扩容开始
先从ArrayList初始化说起, ArrayList有三种方式来初始化
无参, 初始容量设为10, 赋值一个默认容量为空的数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 
携带初始容量参数, 如果>0, 直接初始化一个该容量的数组, = 0赋值为空数组(EMPTY_ELEMENTDATA), <0 抛出非法参数异常 
携带Collection集合, 调用集合的toArray数组方法, 直接赋值为转化后的数组, 之后进行判断, 如果转化后数组>0, 但数组类型不为Object数组则调用Arrays.copyOf()进行转换 ,否则赋值空数组(EMPTY_ELEMENTDATA) 
 
// 此处不理解为什么要判断数组类型可以看这篇博客
elementData.getClass() != Object[].class
private  static  final  int  DEFAULT_CAPACITY  =  10 ;private  static  final  Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public  ArrayList ()  {    this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 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 (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, 实际初始化为一个空数组, 而之后只有到第一次真正添加元素时, 才会真正分配容量, 将数组扩容为10
扩容的起始点是发生在add方法中
每次次增加元素前都会进行ensureCapacityInternal(size + 1)操作, 该方法用于在合适时机进行扩容
public  boolean  add (E e)  {         ensureCapacityInternal(size + 1 );            elementData[size++] = e;     return  true ; } 
 
我们进入ensureCapacityInternal(size + 1), 发现其实际调用了ensureExplicitCapacity()方法
当然在调用之前还调用了一个calculateCapacity
这个方法主要是用来判断ArrayList是否是调用无参构造实例化的, 如果是的话, 就和选择返回默认容量, 否则就是传入的最小容量
其实这里也解答了一个问题就是为什么要定义两个空数组
EMPTY_ELEMENTDATA是初始化时指定大小为0是赋值的, 之后会一步一步按照1.5倍慢慢扩容 
DEFAULTCAPACITY_EMPTY_ELEMENTDATA则是无参默认赋值的, 在第一次增加元素时,会通过calculateCapacity以DEFAULT_CAPACITY(10)进行扩容 
 
private  static  final  Object[] EMPTY_ELEMENTDATA = {};private  static  final  Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
private  void  ensureCapacityInternal (int  minCapacity)  {    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private  static  int  calculateCapacity (Object[] elementData, int  minCapacity)  {         if  (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {         return  Math.max(DEFAULT_CAPACITY, minCapacity);     }          return  minCapacity; } 
 
我们接着跟踪, 来到了 ensureExplicitCapacity, 这里只进行了两个操作, 增加修改次数, 还有判断是否需要扩容
private  void  ensureExplicitCapacity (int  minCapacity)  {    modCount++;          if  (minCapacity - elementData.length > 0 )                  grow(minCapacity); } 
 
终于来到了最核心的方法grow()
private  static  final  int  MAX_ARRAY_SIZE  =  Integer.MAX_VALUE - 8 ;private  void  grow (int  minCapacity)  {         int  oldCapacity  =  elementData.length;               int  newCapacity  =  oldCapacity + (oldCapacity >> 1 );          if  (newCapacity - minCapacity < 0 )         newCapacity = minCapacity;               if  (newCapacity - MAX_ARRAY_SIZE > 0 )         newCapacity = hugeCapacity(minCapacity);          elementData = Arrays.copyOf(elementData, newCapacity); } 
 
由此我们可以梳理出整个扩容的逻辑, 影响的几个分支
第一个是在构造方法中, 选择赋值的是EMPTY_ELEMENTDATA 还是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
第二个是在calculateCapacity()方法中的if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)  return Math.max(DEFAULT_CAPACITY, minCapacity);, 如果是无参默认构造方法赋的初值(也就是DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 那么返回的minCapacity就是
第三个是在grow()方法中的if (newCapacity - minCapacity < 0)   newCapacity = minCapacity;
确认最终选择是扩容大小 为  数组长度的1.5倍大小 还是 方法传入的 minCapacityInternal
面试总结 我在看ArrayList扩容机制,
从他创建对象, 一开始选择的构造函数就开始决定之后的扩容策略了
ArrayList源码中有两个成员变量, 一个是EMPTY_ELEMENTDATA (空元素数据)一个是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认容量空元素数据),这两个都是大小为0的空数组
他俩的区别是什么呢
如果说new  ArrayList 对象时选择的是默认空参构造方法, 则赋值 默认容量空元素数据 , 如果是确定容量为0, 或者是传入一个空的Collection集合, 那么就会赋值为空元素数据, 具体怎样影响扩容策略我们后面说
扩容机制发生在每次增加元素时, 比如add或者addAll方法中, 在增加元素之前都会调用ensureCapacity方法,通过这个方法选在合适的时机扩容合适的大小, 在传入参数时当前输入 + 1
在这个方法里会调用calculateCapacity(), 如果ArrayList在构造时,为默认空参构造, 他就在让传入的当前数组大小 + 1和DefaultCapacity中选择最大的作为minCapacity
这个minCapacity在最后的扩容方法grow()中会当前数组大小的1.5倍进行比较, 选择最大的作为最终的扩容大小
也就是说, 如果你用默认构造函数第一次扩容时,他会给你扩容到10的大小, 如果不是, 而是指定大小为0, 那么就会扩容到 1 的大小(1 -> 2 -> 3 -> 4 -> 6 -> 9 -> 13)会多出很多次扩容, 我想这么做的原因, 可能时源码作者考虑到有些时候不需要10这么大的容量或者扩容机制, 所以如果使用者指定容量大小的话, 那么扩容机制就按照比较 数组容量 + 1 和 数组当前大小1.5的最大值的方式慢慢扩容
缩容机制 既然ArrayList有默认的缩容机制, 那么是否有缩容机制呢? 
答案是没有
无论remove还是clear都不会改变现有数组的大小, 而是将数组的相应位置元素设置为null, 便于垃圾回收器回收, 如果想要缩容, 必须手动去掉哟trimToSize方法, 达到缩容的目的
public  void  trimToSize ()  {        modCount++;                  if  (size < elementData.length) {         	             elementData = (size == 0 )               ? EMPTY_ELEMENTDATA               : Arrays.copyOf(elementData, size);         }     } 
 
源码概览 public  class  ArrayList <E> extends  AbstractList <E>        implements  List <E>, RandomAccess, Cloneable, java.io.Serializable {     private  static  final  long  serialVersionUID  =  8683452581122892189L ;          private  static  final  int  DEFAULT_CAPACITY  =  10 ;          private  static  final  Object[] EMPTY_ELEMENTDATA = {};               private  static  final  Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};          transient  Object[] elementData;           private  int  size;          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;     }          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;         }     }          public  void  trimToSize ()  {         modCount++;         if  (size < elementData.length) {             elementData = (size == 0 )                     ? EMPTY_ELEMENTDATA                     : Arrays.copyOf(elementData, size);         }     }          public  void  ensureCapacity (int  minCapacity)  {                  int  minExpand  =  (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)                                  ? 0                                                    : DEFAULT_CAPACITY;                  if  (minCapacity > minExpand) {             ensureExplicitCapacity(minCapacity);         }     }          private  static  int  calculateCapacity (Object[] elementData, int  minCapacity)  {                  if  (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {             return  Math.max(DEFAULT_CAPACITY, minCapacity);         }                  return  minCapacity;     }          private  void  ensureCapacityInternal (int  minCapacity)  {         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));     }          private  void  ensureExplicitCapacity (int  minCapacity)  {         modCount++;                  if  (minCapacity - elementData.length > 0 )                          grow(minCapacity);     }          private  static  final  int  MAX_ARRAY_SIZE  =  Integer.MAX_VALUE - 8 ;          private  void  grow (int  minCapacity)  {                  int  oldCapacity  =  elementData.length;                           int  newCapacity  =  oldCapacity + (oldCapacity >> 1 );                  if  (newCapacity - minCapacity < 0 )             newCapacity = minCapacity;                                    if  (newCapacity - MAX_ARRAY_SIZE > 0 )             newCapacity = hugeCapacity(minCapacity);                  elementData = Arrays.copyOf(elementData, newCapacity);     }          private  static  int  hugeCapacity (int  minCapacity)  {         if  (minCapacity < 0 )              throw  new  OutOfMemoryError ();         return  (minCapacity > MAX_ARRAY_SIZE) ?                 Integer.MAX_VALUE :                 MAX_ARRAY_SIZE;     }          public  int  size ()  {         return  size;     }          public  boolean  isEmpty ()  {                  return  size == 0 ;     }          public  boolean  contains (Object o)  {                  return  indexOf(o) >= 0 ;     }          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 ;     }          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 ;     }          public  Object clone ()  {         try  {             ArrayList<?> v = (ArrayList<?>) super .clone();                          v.elementData = Arrays.copyOf(elementData, size);             v.modCount = 0 ;             return  v;         } catch  (CloneNotSupportedException e) {                          throw  new  InternalError (e);         }     }          public  Object[] toArray() {         return  Arrays.copyOf(elementData, size);     }          @SuppressWarnings("unchecked")      public  <T> T[] toArray(T[] a) {         if  (a.length < size)                          return  (T[]) Arrays.copyOf(elementData, size, a.getClass());                  System.arraycopy(elementData, 0 , a, 0 , size);         if  (a.length > size)             a[size] = null ;         return  a;     }          @SuppressWarnings("unchecked")      E elementData (int  index)  {         return  (E) elementData[index];     }          public  E get (int  index)  {         rangeCheck(index);         return  elementData(index);     }          public  E set (int  index, E element)  {                  rangeCheck(index);         E  oldValue  =  elementData(index);         elementData[index] = element;                  return  oldValue;     }          public  boolean  add (E e)  {         ensureCapacityInternal(size + 1 );                    elementData[size++] = e;         return  true ;     }          public  void  add (int  index, E element)  {         rangeCheckForAdd(index);         ensureCapacityInternal(size + 1 );                    System.arraycopy(elementData, index, elementData, index + 1 ,                 size - index);         elementData[index] = element;         size++;     }          public  E remove (int  index)  {         rangeCheck(index);         modCount++;         E  oldValue  =  elementData(index);         int  numMoved  =  size - index - 1 ;         if  (numMoved > 0 )             System.arraycopy(elementData, index + 1 , elementData, index,                     numMoved);         elementData[--size] = null ;                   return  oldValue;     }          public  boolean  remove (Object o)  {         if  (o == null ) {             for  (int  index  =  0 ; index < size; index++)                 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)  {         modCount++;         int  numMoved  =  size - index - 1 ;         if  (numMoved > 0 )             System.arraycopy(elementData, index + 1 , elementData, index,                     numMoved);         elementData[--size] = null ;      }          public  void  clear ()  {         modCount++;                  for  (int  i  =  0 ; i < size; i++)             elementData[i] = null ;         size = 0 ;     }          public  boolean  addAll (Collection<? extends E> c)  {         Object[] a = c.toArray();         int  numNew  =  a.length;         ensureCapacityInternal(size + numNew);           System.arraycopy(a, 0 , elementData, size, numNew);         size += numNew;         return  numNew != 0 ;     }          public  boolean  addAll (int  index, Collection<? extends E> c)  {         rangeCheckForAdd(index);         Object[] a = c.toArray();         int  numNew  =  a.length;         ensureCapacityInternal(size + numNew);           int  numMoved  =  size - index;         if  (numMoved > 0 )             System.arraycopy(elementData, index, elementData, index + numNew,                     numMoved);         System.arraycopy(a, 0 , elementData, index, numNew);         size += numNew;         return  numNew != 0 ;     }          protected  void  removeRange (int  fromIndex, int  toIndex)  {         modCount++;         int  numMoved  =  size - toIndex;         System.arraycopy(elementData, toIndex, elementData, fromIndex,                 numMoved);                  int  newSize  =  size - (toIndex - fromIndex);         for  (int  i  =  newSize; i < size; i++) {             elementData[i] = null ;         }         size = newSize;     }          private  void  rangeCheck (int  index)  {         if  (index >= size)             throw  new  IndexOutOfBoundsException (outOfBoundsMsg(index));     }          private  void  rangeCheckForAdd (int  index)  {         if  (index > size || index < 0 )             throw  new  IndexOutOfBoundsException (outOfBoundsMsg(index));     }          private  String outOfBoundsMsg (int  index)  {         return  "Index: "  + index + ", Size: "  + size;     }          public  boolean  removeAll (Collection<?> c)  {         Objects.requireNonNull(c);                  return  batchRemove(c, false );     }          public  boolean  retainAll (Collection<?> c)  {         Objects.requireNonNull(c);         return  batchRemove(c, true );     }          public  ListIterator<E> listIterator (int  index)  {         if  (index < 0  || index > size)             throw  new  IndexOutOfBoundsException ("Index: "  + index);         return  new  ListItr (index);     }          public  ListIterator<E> listIterator ()  {         return  new  ListItr (0 );     }          public  Iterator<E> iterator ()  {         return  new  Itr ();     }