ArrayList源码分析

一、定义

  ArrayList底层是由线性数组来实现的动态数组。新建一个ArrayList时,就是在底层新建一个线性数组。ArrayList的默认初始的数组长度为10(JDK1.8);

二、关注点

  1、ArrayList集合的数据是可以为空的;

  2、数据有序(集合中数据的顺序和添加数据的顺序相同),从头向尾插入数据;

  3、线程不安全(多线程同时操作某一个指定集合时,共享数据存在线程安全 问题)

三、ArrayList集合API

  1、新增元素

   1)在集合中追加元素   

 1 protected transient int modCount = 0;
 2 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 3 public boolean add(E e) {
 4       //计数器
 5     modCount++;
 6       //新增元素操作
 7     add(e, elementData, size);
 8     return true;
 9 }
10 
11 private void add(E e, Object[] elementData, int s) {
12   if (s == elementData.length){
13     //数组已满,扩展数组
14     elementData = grow();
15   }
16   //将E放置在数组索引为S的位置
17   elementData[s] = e;
18   //将整个数组的大小加1
19   size = s + 1;
20 }
21 
22 private Object[] grow() {
23   //在原始数组长度的基础上加1
24   return grow(size + 1);
25 }
26 
27 private Object[] grow(int minCapacity) {
28   //获取原始数组的长度,赋值给oldCapacity
29   int oldCapacity = elementData.length;
30   //判空操作(原始数组的长度大于0,或者该数组不为空数组)
31   if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)    
32     int newCapacity = ArraysSupport.newLength(oldCapacity,
33                                 // minimum growth 按照上面代码逻辑差值是1
34                                 minCapacity - oldCapacity,
35                                 // preferred growth oldCapacity对2取余
36                                 oldCapacity >> 1);
37     return elementData = Arrays.copyOf(elementData, newCapacity);
38   } else {
39       //新建一个Object数组,长度为默认长度10和最小扩展量的最大值
40     return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
41   }
42 }
43 
44 //扩容算法
45 public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
46 public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
47   //保证最小扩展量为1,因为minGrowth一直为1。若preGrowth大于minGrowth,则取preGrowth,否则为1;
48   int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
49   if (newLength - MAX_ARRAY_LENGTH <= 0) {
50     //若扩展值小于MAX_ARRAY_LENGTH,则直接返回扩展值
51     return newLength;
52   }
53   //当newLength的大小(最大扩容量)大于等于Integer.MAX-8的值后,使用该方法
54   return hugeLength(oldLength, minGrowth);
55 }
56 
57 private static int hugeLength(int oldLength, int minGrowth) {
58   //获取最小扩增量1
59   int minLength = oldLength + minGrowth;
60   if (minLength < 0) { // overflow
61     throw new OutOfMemoryError("Required array length too large");
62   }
63   //原始容量+1后的值(最小扩容量)小于等于integer最大值-8
64   if (minLength <= MAX_ARRAY_LENGTH) {
65     //返回Integer的最大值-8
66     return MAX_ARRAY_LENGTH;
67   }
68   //最小扩容量大于Integre最大值-8时,直接返回Integrer的最大值
69   return Integer.MAX_VALUE;
70 }
View Code

   2)在集合中指定索引位置添加元素  

 1 public void add(int index, E element) {
 2       //判断添加的索引是否在数组中
 3     rangeCheckForAdd(index);
 4     modCount++;
 5     final int s;
 6     Object[] elementData;
 7       //判断原始数组大小等于数组中元素的长度
 8     if ((s = size) == (elementData = this.elementData).length){
 9           //执行扩容操作
10         elementData = grow();
11     }
12       //数组复制,将原数组(第一个elementData)从index的索引位置开始复制一系列元素A(长度为s-index)
13       //复制到新数组(第二个elementData),将A从index+1的位置开始复制。完成后就将index位置空置出来
14     System.arraycopy(elementData, index,
15                      elementData, index + 1,
16                      s - index);
17       //将元素放置在index的位置
18     elementData[index] = element;
19       //将数组长度加1;
20     size = s + 1;
21 }
22 
23 private void rangeCheckForAdd(int index) {
24   if (index > size || index < 0)
25     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
26 }
View Code

     3)删除指定索引的元素  

 1 public E remove(int index) {
 2       //检查索引有没有越界
 3     Objects.checkIndex(index, size);
 4       //对数据做一个备份
 5     final Object[] es = elementData;
 6       //获取要删除的数据,用户结果返回
 7     @SuppressWarnings("unchecked") E oldValue = (E) es[index];
 8       //删除数组中数据
 9     fastRemove(es, index);
10       //返回被删除的数据
11     return oldValue;
12 }
13 
14 private void fastRemove(Object[] es, int i) {
15   modCount++;
16   final int newSize;
17   //判断被删除元素后面是否还有元素
18   if ((newSize = size - 1) > i){
19     //从原数组(第一个es)的第i+1索引处开始复制后面的所有元素
20     //新数组(第二个es),将旧数组复制的元素在新数组中从i的索引位置开始复制元素到新数组
21     System.arraycopy(es, i + 1, es, i, newSize - i);
22   }
23   //如果是最后一个元素或者执行完上一步的if语句,直接将最后一个索引的位置设置为null,方便GC回收内存
24   es[size = newSize] = null;
25 }
View Code

   4)删除集合中指定的元素

 1 public boolean remove(Object o) {
 2     final Object[] es = elementData;
 3     final int size = this.size;
 4     int i = 0;
 5       //获取对应元素的索引值i
 6     found: {
 7         if (o == null) {
 8               //遍历整个数组,获取第一个元素为null的索引值i
 9             for (; i < size; i++)
10                 if (es[i] == null)
11                     break found;
12         } else {
13             for (; i < size; i++)
14                   //获取第一个元素值为o的索引值i
15                 if (o.equals(es[i]))
16                     break found;
17         }
18           //集合中不存在该元素,直接返回false
19         return false;
20     }
21       //删除指定索引的元素
22     fastRemove(es, i);
23     return true;
24 }
View Code

四、ArrayLsit的优缺点

  1、优点

    1)ArrayList的查询元素的速度是非常快的,由于数组是由索引存在的,直接根绝索引查询即可(指哪打哪)。由于ArrayList实现了RandomAccess接口,所以ArrayList被标记为使用二分法进行查询(使用二分法找到指定的索引);

    2)ArrayList在最后的位置添加(删除)元素时,可以直接操作,时间复杂度为0(1);

  2、缺点

    在集合的指定位置(非最后的位置)进行添加和删除元素时,都要重新复制数组。增加空间复杂度O(n),比较耗费性能;

五、为什么ArrayList集合中定义的数组使用elementData[],使用transient关键字进行修饰?

  由于ArrayList实现了Serializable接口,则该集合允许进行序列化和反序列化。对于未满的数组来说,真正需要序列化的只有那几个不为null的元素,其余的全部为null,序列化也没有意义。所以为了增强序列化(反序列化)的性能,使用transient来修饰,对数组不进行序列化,然后重写Serializable接口的WriteObject和ReadObject方法,使用重写的方法进行序列化和反序列化,保证只序列化或者反序列化存在的元素;

 1 @java.io.Serial
 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       //先调用默认的序列化方法,序列化没有被Transient修饰的元素
 7     s.defaultWriteObject();
 8 
 9     // Write out size as capacity for behavioral compatibility with clone()
10     s.writeInt(size);
11 
12     // Write out all elements in the proper order.
13     for (int i=0; i<size; i++) {
14           //只序列化存在的元素
15         s.writeObject(elementData[i]);
16     }
17 
18     if (modCount != expectedModCount) {
19         throw new ConcurrentModificationException();
20     }
21 }
22 
23 @java.io.Serial
24 private void readObject(java.io.ObjectInputStream s)
25     throws java.io.IOException, ClassNotFoundException {
26 
27     // Read in size, and any hidden stuff
28     s.defaultReadObject();
29 
30     // Read in capacity
31     s.readInt(); // ignored
32 
33     if (size > 0) {
34         // like clone(), allocate array based upon size not capacity
35         SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
36         Object[] elements = new Object[size];
37 
38         // Read in all elements in the proper order.
39         for (int i = 0; i < size; i++) {
40             elements[i] = s.readObject();
41         }
42 
43         elementData = elements;
44     } else if (size == 0) {
45         elementData = EMPTY_ELEMENTDATA;
46     } else {
47         throw new java.io.InvalidObjectException("Invalid size: " + size);
48     }
49 }
View Code

 

原文地址:https://www.cnblogs.com/wwcxBlog/p/13093443.html