zoukankan      html  css  js  c++  java
  • ArrayList源码分析

    写在前面

    本文是针对Java 1.8的源代码进行解析的,可能会和其他版本有所出入。

    全局变量

    1. 默认容量

    [java] view plain copy
     
     print?
    1. private static final int DEFAULT_CAPACITY = 10;  

    2. 空的对象数组

    [java] view plain copy
     
     print?
    1. private static final Object[] EMPTY_ELEMENTDATA = {};  

    3.默认的空数组

    [java] view plain copy
     
     print?
    1. // 无参构造函数创建的数组  
    2. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  


    4.存放数据的数组的缓存变量,不可序列化

    [java] view plain copy
     
     print?
    1. transient Object[] elementData;  


    5.数组的大小

    [java] view plain copy
     
     print?
    1. private int size;  

    构造方法

    1.带有容量initialCapacity的构造方法

    源码解释:

    [java] view plain copy
     
     print?
    1. public ArrayList(int initialCapacity) {  
    2.      // 如果初始化时ArrayList大小大于0  
    3.     if (initialCapacity > 0) {  
    4.           // new一个该大小的object数组赋给elementData  
    5.         this.elementData = new Object[initialCapacity];  
    6.     } else if (initialCapacity == 0) { // 如果大小为0  
    7.           // 将空数组赋给elementData  
    8.         this.elementData = EMPTY_ELEMENTDATA;  
    9.     } else { // 小于0  
    10.           // 则抛出IllegalArgumentException异常  
    11.         throw new IllegalArgumentException("Illegal Capacity: "+  
    12.                                            initialCapacity);  
    13.     }  
    14. }  

    2.不带参数的构造方法

    源码解释:

    [java] view plain copy
     
     print?
    1. public ArrayList() {  
    2.      // 直接将空数组赋给elementData    
    3.     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  
    4. }  

    3.带参数Collection的构造方法

    源码解释:
    参数c为一个Collection,Collection的实现类大概有以下几种常用类型:

    • List:元素可以重复的容器
    • Set: 元素不可重复的容器
    • Queue:结构是一个队列,先进先出

    这个构造方法的意思是,将一个Collection实现类的对象转换为一个ArrayList,但是c容器装的内容必须为ArrayList装的内容的子类。例如,将一个装了String内容的HashSet转换为装了String内容的ArrayList,使得ArrayList的大小和值数组都是HashSet的大小和值数组。具体实现如下代码,首先调用c(Collection的具体实现类)的toArray方法,具体大家可以看各个实现类的toArray方法,但是大概意思都是将c容器转换为object类型的数组,因为它们的返回值都是object[]。之于下面的两个判断是当得到的elementData的类名不是Object类名的时候或者是长度为0的时候才会执行。

    [java] view plain copy
     
     print?
    1. public ArrayList(Collection<? extends E> c) {  
    2.     elementData = c.toArray();  
    3.     if ((size = elementData.length) != 0) {  
    4.         if (elementData.getClass() != Object[].class)  
    5.             elementData = Arrays.copyOf(elementData, size, Object[].class);  
    6.     } else {  
    7.         // replace with empty array.  
    8.         this.elementData = EMPTY_ELEMENTDATA;  
    9.     }  
    10. }  

    方法

    1.trimToSize()

    说明:将ArrayList的容量设置为当前size的大小。首先需要明确一个概念,ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。ArrayList每次都会预申请多一点空间,以便添加元素的时候不需要每次都进行扩容操作,例如我们的元素个数是10个,它申请的内存空间必定会大于10,即length>size,而这个方法就是把ArrayList的内存空间设置为size,去除没有用到的null值空间。这也就是我们为什么每次在获取数据长度是都是调用list.size()而不是list.length()。

    源码解释:首先modCount是从类 Java.util.AbstractList 继承的字段,这个字段主要是为了防止在多线程操作的情况下,List发生结构性的变化,什么意思呢?就是防止一个线程正在迭代,另外一个线程进行对List进行remove操作,这样当我们迭代到最后一个元素时,很明显此时List的最后一个元素为空,那么这时modCount就会告诉迭代器,让其抛出异常 ConcurrentModificationException。

    如果没有这一个变量,那么系统肯定会报异常ArrayIndexOutOfBoundsException,这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该考虑的),为了避免出现这样的异常,定义了检查。
    (引用自:郭无心,详情可以看他在知乎的回答:https://www.zhihu.com/question/24086463/answer/64717159)。

    [java] view plain copy
     
     print?
    1. public void trimToSize() {  
    2.     modCount++;  
    3.      // 如果size小于length  
    4.     if (size < elementData.length) {  
    5.          // 重新将elementData设置大小为size  
    6.         elementData = (size == 0)  
    7.           ? EMPTY_ELEMENTDATA  
    8.           : Arrays.copyOf(elementData, size);  
    9.     }  
    10. }  

    2.size()

    说明:返回ArrayList的大小
    源码解释:直接返回size

    [java] view plain copy
     
     print?
    1. public int size() {  
    2.     return size;  
    3. }  

    3.isEmpty()

    说明:返回是否为空
    源码解释: 直接返回判断size==0

    [java] view plain copy
     
     print?
    1. public boolean isEmpty() {  
    2.     return size == 0;  
    3. }  

    4.indexOf(Object o)

    说明:对象o在ArrayList中的下标位置,如果存在返回位置i,不存在返回-1
    源码解释:遍历ArrayList的大小,比较o和容器内的元素,若相等,则返回位置i,若遍历完都不相等,返回-1

    [java] view plain copy
     
     print?
    1. public int indexOf(Object o) {  
    2.     if (o == null) {  
    3.         for (int i = 0; i < size; i++)  
    4.             if (elementData[i]==null)  
    5.                 return i;  
    6.     } else {  
    7.         for (int i = 0; i < size; i++)  
    8.             if (o.equals(elementData[i]))  
    9.                 return i;  
    10.     }  
    11.     return -1;  
    12. }  

    5.contains(Object o)

    说明:是否包含对象o
    源码解释:调用indexOf()方法得到下标,存在则下标>=0,不存在为-1,即只要比较下标和0的大小即可。

    [java] view plain copy
     
     print?
    1. public boolean contains(Object o) {  
    2.     return indexOf(o) >= 0;  
    3. }  

    6.lastIndexOf(Object o)

    说明:返回容器内出现o的最后一个位置

    源码解释:从后向前遍历,得到第一个出现对象o的位置,不存在则返回-1

    [java] view plain copy
     
     print?
    1. public int lastIndexOf(Object o) {  
    2.     if (o == null) {  
    3.         for (int i = size-1; i >= 0; i--)  
    4.             if (elementData[i]==null)  
    5.                 return i;  
    6.     } else {  
    7.         for (int i = size-1; i >= 0; i--)  
    8.             if (o.equals(elementData[i]))  
    9.                 return i;  
    10.     }  
    11.     return -1;  
    12. }  

    7.clone()

    说明:返回此 ArrayList 实例的浅表副本。

    源码解释:

    [java] view plain copy
     
     print?
    1. public Object clone() {  
    2.     try {  
    3.          // 调用父类(翻看源码可见是Object类)的clone方法得到一个ArrayList副本  
    4.         ArrayList<?> v = (ArrayList<?>) super.clone();  
    5.          // 调用Arrays类的copyOf,将ArrayList的elementData数组赋值给副本的elementData数组  
    6.         v.elementData = Arrays.copyOf(elementData, size);  
    7.         v.modCount = 0;  
    8.          // 返回副本v  
    9.         return v;  
    10.     } catch (CloneNotSupportedException e) {  
    11.         throw new InternalError(e);  
    12.     }  
    13.  }  

    8.toArray()

    说明:ArrayList 实例转换为。
    源码解释:直接调用Arrays类的copyOf。

    [java] view plain copy
     
     print?
    1. public Object[] toArray() {  
    2.     return Arrays.copyOf(elementData, size);  
    3. }  

    9.toArray(T[] a)

    说明:将ArrayList里面的元素赋值到一个数组中去
    源码解释:如果a的长度小于ArrayList的长度,直接调用Arrays类的copyOf,返回一个比a数组长度要大的新数组,里面元素就是ArrayList里面的元素;如果a的长度比ArrayList的长度大,那么就调用System.arraycopy,将ArrayList的elementData数组赋值到a数组,然后把a数组的size位置赋值为空。

    [java] view plain copy
     
     print?
    1. public <T> T[] toArray(T[] a) {  
    2.     if (a.length < size)  
    3.         // Make a new array of a's runtime type, but my contents:  
    4.         return (T[]) Arrays.copyOf(elementData, size, a.getClass());  
    5.     System.arraycopy(elementData, 0, a, 0, size);  
    6.     if (a.length > size)  
    7.         a[size] = null;  
    8.     return a;  
    9.  }    

    10.rangeCheck(int index)

    说明:测试index是否越界
    源码解释:

    [java] view plain copy
     
     print?
    1. private void rangeCheck(int index) {  
    2.      // 如果下标超过ArrayList的数组长度  
    3.     if (index >= size)  
    4.          // 抛出IndexOutOfBoundsException异常  
    5.         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));  
    6. }  

    11.get(int index)

    说明:获取index位置的元素
    源码解释:先检查是否越界,然后返回ArrayList的elementData数组index位置的元素。

    [java] view plain copy
     
     print?
    1. public E get(int index) {  
    2.      // 检查是否越界  
    3.     rangeCheck(index);  
    4.      // 返回ArrayList的elementData数组index位置的元素  
    5.     return elementData(index);  
    6. }  

    12.set(int index, E element)

    说明:设置index位置的元素值了element,返回该位置的之前的值
    源码解释:

    [java] view plain copy
     
     print?
    1. public E set(int index, E element) {  
    2.      // 检查是否越界    
    3.     rangeCheck(index);  
    4.      // 调用elementData(index)获取到当前位置的值  
    5.     E oldValue = elementData(index);  
    6.      // 将element赋值到ArrayList的elementData数组的第index位置  
    7.     elementData[index] = element;  
    8.     return oldValue;  
    9. }  

    13.ensureCapacityInternal(int minCapacity)

    说明:得到最小扩容量
    源码解释:

    [java] view plain copy
     
     print?
    1. private void ensureCapacityInternal(int minCapacity) {  
    2.     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  
    3.          // 获取默认的容量和传入参数的较大值  
    4.         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  
    5.     }  
    6.     ensureExplicitCapacity(minCapacity);  
    7. }  

    14.ensureExplicitCapacity(int minCapacity)

    说明:判断是否需要扩容
    源码解释:

    [java] view plain copy
     
     print?
    1. private void ensureExplicitCapacity(int minCapacity) {  
    2.     modCount++;  
    3.     // 如果最小需要空间比elementData的内存空间要大,则需要扩容  
    4.     if (minCapacity - elementData.length > 0)  
    5.         grow(minCapacity);  
    6. }  

    15.grow()方法

    说明:帮助ArrayList动态扩容的核心方法
    源码解释:

    [java] view plain copy
     
     print?
    1. // MAX_VALUE为231-1,MAX_ARRAY_SIZE 就是获取Java中int的最大限制,以防止越界    
    2. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  
    3.   
    4.   
    5. private void grow(int minCapacity) {  
    6.     // 获取到ArrayList中elementData数组的内存空间长度  
    7.     int oldCapacity = elementData.length;  
    8.     // 扩容至原来的1.5倍  
    9.     int newCapacity = oldCapacity + (oldCapacity >> 1);  
    10.     // 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,   
    11.     // 不够就将数组长度设置为需要的长度  
    12.     if (newCapacity - minCapacity < 0)  
    13.         newCapacity = minCapacity;  
    14.     // 判断有没超过最大限制  
    15.     if (newCapacity - MAX_ARRAY_SIZE > 0)  
    16.         newCapacity = hugeCapacity(minCapacity);  
    17.     // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间  
    18.     // 并将elementData的数据复制到新的内存空间  
    19.     elementData = Arrays.copyOf(elementData, newCapacity);  
    20. }  

    16.add(E e)

    说明:添加元素e
    源码解释:

    [java] view plain copy
     
     print?
    1. public boolean add(E e) {  
    2.      // 扩容  
    3.     ensureCapacityInternal(size + 1);    
    4.     // 将e赋值给elementData的size+1的位置。  
    5.     elementData[size++] = e;  
    6.     return true;  
    7. }  

    17.add(int index, E element)

    说明:在ArrayList的index位置,添加元素element
    源码解释:

    [java] view plain copy
     
     print?
    1. public void add(int index, E element) {  
    2.     // 判断index是否越界    
    3.     rangeCheckForAdd(index);  
    4.      // 扩容  
    5.     ensureCapacityInternal(size + 1);    
    6.      // 将elementData从index位置开始,复制到elementData的index+1开始的连续空间  
    7.     System.arraycopy(elementData, index, elementData, index + 1,  
    8.                      size - index);  
    9.      // 在elementData的index位置赋值element  
    10.     elementData[index] = element;  
    11.      // ArrayList的大小加一    
    12.     size++;  
    13. }  

    18.remove(int index)

    说明:在ArrayList的移除index位置的元素
    源码解释:

    [java] view plain copy
     
     print?
    1. public E remove(int index) {  
    2.      // 判断是否越界    
    3.     rangeCheck(index);  
    4.     modCount++;  
    5.      // 读取旧值    
    6.     E oldValue = elementData(index);  
    7.      // 获取index位置开始到最后一个位置的个数  
    8.     int numMoved = size - index - 1;  
    9.     if (numMoved > 0)  
    10.          // 将elementData数组index+1位置开始拷贝到elementData从index开始的空间  
    11.         System.arraycopy(elementData, index+1, elementData, index,  
    12.                          numMoved);  
    13.      // 使size-1 ,设置elementData的size位置为空,让GC来清理内存空间  
    14.     elementData[--size] = null; // clear to let GC do its work  
    15.     return oldValue;  
    16. }  

    19.remove(Object o)

    说明:在ArrayList的移除对象为O的元素,跟indexOf方法思想基本一致
    源码解释:

    [java] view plain copy
     
     print?
    1. public boolean remove(Object o) {  
    2.     if (o == null) {  
    3.         for (int index = 0; index < size; index++)  
    4.             if (elementData[index] == null) {  
    5.                 fastRemove(index);  
    6.                 return true;  
    7.             }  
    8.     } else {  
    9.         for (int index = 0; index < size; index++)  
    10.             if (o.equals(elementData[index])) {  
    11.                 fastRemove(index);  
    12.                 return true;  
    13.             }  
    14.     }  
    15.     return false;  
    16. }  

    20.clear()

    说明:设置全部元素为null值,并设置size为0。
    源码解释:可见clear操作并不是从空间内删除,只是设置为null值,等待垃圾回收机制来回收而已,把size设置为0,以便我们不会浏览到null值的内存空间。

    [java] view plain copy
     
     print?
    1. public void clear() {  
    2.     modCount++;  
    3.     // clear to let GC do its work  
    4.     for (int i = 0; i < size; i++)  
    5.         elementData[i] = null;  
    6.     size = 0;  
    7. }  

    21.addAll(Collection<? extends E> c)

    说明:将Collection c的全部元素添加到ArrayList中
    源码解释:

    [java] view plain copy
     
     print?
    1. public boolean addAll(Collection<? extends E> c) {  
    2.      // 将c转换为数组a  
    3.     Object[] a = c.toArray();  
    4.      // 获取a占的内存空间长度赋值给numNew  
    5.     int numNew = a.length;  
    6.      // 扩容至size + numNew  
    7.     ensureCapacityInternal(size + numNew);  // Increments modCount  
    8.      // 将a的第0位开始拷贝至elementData的size位开始,拷贝长度为numNew  
    9.     System.arraycopy(a, 0, elementData, size, numNew);  
    10.      // 将size增加numNew    
    11.     size += numNew;  
    12.      // 如果c为空,返回false,c不为空,返回true  
    13.     return numNew != 0;  
    14. }  

    22.addAll(int index, Collection<? extends E> c)

    说明:从第index位开始,将c全部拷贝到ArrayList
    源码解释:

    [java] view plain copy
     
     print?
    1. public boolean addAll(int index, Collection<? extends E> c) {  
    2.      // 判断index大于size或者是小于0,如果是,则抛出IndexOutOfBoundsException异常  
    3.     rangeCheckForAdd(index);  
    4.      // 将c转换为数组a  
    5.     Object[] a = c.toArray();  
    6.     int numNew = a.length;  
    7.      // 扩容至size + numNew  
    8.     ensureCapacityInternal(size + numNew);  // Increments modCount  
    9.       // 获取需要添加的个数  
    10.     int numMoved = size - index;  
    11.     if (numMoved > 0)  
    12.         System.arraycopy(elementData, index, elementData, index + numNew,  
    13.                          numMoved);  
    14.     System.arraycopy(a, 0, elementData, index, numNew);  
    15.     size += numNew;  
    16.     return numNew != 0;  
    17. }  

    24.batchRemove(Collection<?> c, boolean complement)

    说明:根据complement值,将ArrayList中包含c中元素的元素删除或者保留
    源码解释:

    [java] view plain copy
     
     print?
    1. private boolean batchRemove(Collection<?> c, boolean complement) {  
    2.     final Object[] elementData = this.elementData;  
    3.      // 定义一个w,一个r,两个同时右移     
    4.     int r = 0, w = 0;  
    5.     boolean modified = false;  
    6.     try {  
    7.          // r先右移  
    8.         for (; r < size; r++)  
    9.               // 如果c中不包含elementData[r]这个元素  
    10.             if (c.contains(elementData[r]) == complement)  
    11.                   // 则直接将r位置的元素赋值给w位置的元素,w自增  
    12.                 elementData[w++] = elementData[r];  
    13.     } finally {  
    14.         // 防止抛出异常导致上面r的右移过程没完成  
    15.         if (r != size) {  
    16.               // 将r未右移完成的位置的元素赋值给w右边位置的元素  
    17.             System.arraycopy(elementData, r,  
    18.                              elementData, w,  
    19.                              size - r);  
    20.               // 修改w值增加size-r  
    21.             w += size - r;  
    22.         }  
    23.         if (w != size) {  
    24.             // 如果有被覆盖掉的元素,则将w后面的元素都赋值为null  
    25.             for (int i = w; i < size; i++)  
    26.                 elementData[i] = null;  
    27.             modCount += size - w;  
    28.               // 修改size为w  
    29.             size = w;  
    30.             modified = true;  
    31.         }  
    32.     }  
    33.     return modified;  
    34. }  

    25.removeAll(Collection<?> c)

    说明:ArrayList移除c中的所有元素
    源码解释:

    [java] view plain copy
     
     print?
    1. public boolean removeAll(Collection<?> c) {  
    2.      // 如果c为空,则抛出空指针异常  
    3.     Objects.requireNonNull(c);  
    4.      // 调用batchRemove移除c中的元素  
    5.     return batchRemove(c, false);  
    6. }  

    26.retainAll(Collection<?> c)

    说明:和removeAll相反,仅保留c中所有的元素

    源码解释:

    [java] view plain copy
     
     print?
    1. public boolean retainAll(Collection<?> c) {  
    2.     Objects.requireNonNull(c);  
    3.      // 调用batchRemove保留c中的元素  
    4.     return batchRemove(c, true);  
    5. }  

    27.iterator()

    说明:返回一个Iterator对象,Itr为ArrayList的一个内部类,其实现了Iterator<E>接口

    代码解释:

    [java] view plain copy
     
     print?
    1. public Iterator<E> iterator() {  
    2.     return new Itr();  
    3. }  

    28.listIterator()

    说明:返回一个ListIterator对象,ListItr为ArrayList的一个内部类,其实现了ListIterator<E> 接口
    源码解释:

    [java] view plain copy
     
     print?
    1. public ListIterator<E> listIterator() {  
    2.     return new ListItr(0);  
    3. }  

    29.listIterator(int index)

    说明:返回一个从index开始的ListIterator对象
    源码解释:

    [java] view plain copy
     
     print?
    1. public ListIterator<E> listIterator(int index) {  
    2.     if (index < 0 || index > size)  
    3.         throw new IndexOutOfBoundsException("Index: "+index);  
    4.     return new ListItr(index);  
    5. }  

    30.subList(int fromIndex, int toIndex)

    说明:根据两个参数,获取到一个子序列
    源码解释:

    [java] view plain copy
     
     print?
    1. public List<E> subList(int fromIndex, int toIndex) {  
    2.      // 检查异常  
    3.     subListRangeCheck(fromIndex, toIndex, size);  
    4.      // 调用SubList类的构造方法  
    5.     return new SubList(this, 0, fromIndex, toIndex);  
    6. }  

    内部类

    [java] view plain copy
     
     print?
    1. (1)private class Itr implements Iterator<E>  
    2. (2)private class ListItr extends Itr implements ListIterator<E>  
    3. (3)private class SubList extends AbstractList<E> implements RandomAccess  
    4. (4)static final class ArrayListSpliterator<E> implements Spliterator<E>  


    ArrayList有四个内部类,

    其中的Itr是实现了Iterator接口,同时重写了里面的hasNext(),next(),remove()等方法;

    其中的ListItr继承Itr,实现了ListIterator接口,同时重写了hasPrevious(),nextIndex(),

    previousIndex(),previous(),set(E e),add(E e)等方法,所以这也可以看出了Iterator和ListIterator的区别,就是ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。具体可以参考http://blog.csdn.net/a597926661/article/details/7679765

    其中的SubList继承AbstractList,实现了RandmAccess接口,类内部实现了对子序列的增删改查等方法,但它同时也充分利用了内部类的优点,就是共享ArrayList的全局变量,例如检查器变量modCount,数组elementData等,所以SubList进行的增删改查操作都是对ArrayList的数组进行的,并没有创建新的数组。

    最后一个比较个人比较少接触,大家需要自行度娘。

    End

    笔者技术真的是一般般,写这个为了加深理解的同时给害怕看源代码的朋友一点鼓励,所以笔者在写的过程中有查阅很多资料来努力减少错误,但是如有错漏之处,希望大神们指出,我会第一时间修改,以免误人子弟,也希望和笔者一样基础不够好的朋友不要畏惧看源码,源码看起来并不会很难,而且多看源代码会对Java更深刻的理解。

  • 相关阅读:
    微信助力活动表结构设计
    mysql的in查询分析
    PHP从数组中找到指定元素的位置
    Java程序使用Alpine Linux报错java.lang.NoClassDefFoundError: Could not initialize class org.xerial.snappy.Snappy解决
    Eclipse/Idea 代码格式化部分忽略
    修改Linux桌面高分屏下QT程序界面的缩放
    折腾linux随笔 之 关闭Budgie默认自动隐藏应用的菜单栏 与 Gnome系桌面应用菜单无内容解决
    Debian Buster 使用Lxde在界面中打开url提示错误解决
    Portainer容器可视化管理工具使用文档
    Lxde添加触摸板双击功能、防误触
  • 原文地址:https://www.cnblogs.com/zedosu/p/6662196.html
Copyright © 2011-2022 走看看