基于JDK8的ArrayList源码分析

Posted 不如打代码

tags:

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

基于JDK8的ArrayList源码分析

ArrayList它能够自动扩展大小以适应存储元素的不断增加。它的底层是基于数组实现的,因此它具有数组的一些特点,例如查找修改快而插入删除慢。ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。本篇我们将深入源码看看它是怎样对数组进行封装的。

一、类的声明

它继承了AbstractList类,实现了List、RandomAccess、Cloneable、Serializable接口。

 
   
   
 
  1. public class ArrayList<E>

  2. extends AbstractList<E>

  3. implements List<E>, RandomAccess, Cloneable, Serializable

二、构造器

ArrayList拥有三个构造器,无参构造方法构造的ArrayList的容量默认为10,带有Collection参数的构造方法,将Collection转化为数组赋给ArrayList的实现数组elementData。

 
   
   
 
  1. /**

  2.     * 默认初始化容量

  3.     */

  4.    private static final int DEFAULT_CAPACITY = 10;

  5.    /**

  6.     * 用于空实例的共享空数组实例

  7.     */

  8.    private static final Object[] EMPTY_ELEMENTDATA = {};

  9.    /**

  10.     * 用于默认大小的空实例的共享空数组实例。 我们将它与EMPTY ELEMENTDATA区分开来,以便知道在添加第一个元素时要扩容多少。

  11.     */

  12.    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

  13.    /**

  14.     * The array buffer into which the elements of the ArrayList are stored.

  15.     * The capacity of the ArrayList is the length of this array buffer. Any

  16.     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA

  17.     * will be expanded to DEFAULT_CAPACITY when the first element is added.

  18.     * transient 说明这个数组无法序列化

  19.     */

  20.    transient Object[] elementData; // non-private to simplify nested class access

  21.    /**

  22.     * The size of the ArrayList (the number of elements it contains).

  23.     *

  24.     * @serial

  25.     */

  26.    private int size;

  27.    /**

  28.     * Constructs an empty list with the specified initial capacity.

  29.     *

  30.     * @param  initialCapacity  the initial capacity of the list

  31.     * @throws IllegalArgumentException if the specified initial capacity

  32.     *         is negative

  33.     */

  34.    public ArrayList(int initialCapacity) {

  35.        if (initialCapacity > 0) {

  36.            this.elementData = new Object[initialCapacity];

  37.        } else if (initialCapacity == 0) {

  38.            this.elementData = EMPTY_ELEMENTDATA;

  39.        } else {

  40.            throw new IllegalArgumentException("Illegal Capacity: "+

  41.                                               initialCapacity);

  42.        }

  43.    }

  44.    /**

  45.     * Constructs an empty list with an initial capacity of ten.

  46.     */

  47.    public ArrayList() {

  48.        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

  49.    }

  50.    /**

  51.     * Constructs a list containing the elements of the specified

  52.     * collection, in the order they are returned by the collection's

  53.     * iterator.

  54.     *

  55.     * @param c the collection whose elements are to be placed into this list

  56.     * @throws NullPointerException if the specified collection is null

  57.     */

  58.    public ArrayList(Collection<? extends E> c) {

  59.        elementData = c.toArray();

  60.        if ((size = elementData.length) != 0) {

  61.            // c.toArray might (incorrectly) not return Object[] (see 6260652)

  62.            if (elementData.getClass() != Object[].class)

  63.                elementData = Arrays.copyOf(elementData, size, Object[].class);

  64.        } else {

  65.            // replace with empty array.

  66.            this.elementData = EMPTY_ELEMENTDATA;

  67.        }

  68.    }

 
   
   
 

三、add操作

向ArrayList中添加数据的操作有四中,分别为:

  • add(E e)

  • add(int index, E element)

  • addAll(Collection c)

  • addAll(int index, Collection c)

  1. add(E e)
    其对应的源代码如下:

 
   
   
 
  1.    public boolean add(E e) {

  2.            ensureCapacityInternal(size + 1);  // Increments modCount!!

  3.            elementData[size++] = e;

  4.            return true;

  5.        }

非常简单的一段代码,其流程如下:

  • 判断容量及做扩容操作

  • 将对象添加到size的位置,并将size加1

  • 返回结果
    进入ensureCapacityInternal看看明白哈哈。

 
   
   
 
  1. private void ensureCapacityInternal(int minCapacity) {

  2.        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

  3.            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

  4.        }

  5.        ensureExplicitCapacity(minCapacity);

  6.    }

  7. private void ensureExplicitCapacity(int minCapacity) {

  8.        modCount++;

  9.        // overflow-conscious code

  10.        if (minCapacity - elementData.length > 0)

  11.            grow(minCapacity);

  12.    }

  13. private void grow(int minCapacity) {

  14.        // overflow-conscious code

  15.        int oldCapacity = elementData.length;

  16.        int newCapacity = oldCapacity + (oldCapacity >> 1);

  17.        if (newCapacity - minCapacity < 0)

  18.            newCapacity = minCapacity;

  19.        if (newCapacity - MAX_ARRAY_SIZE > 0)

  20.            newCapacity = hugeCapacity(minCapacity);

  21.        // minCapacity is usually close to size, so this is a win:

  22.        elementData = Arrays.copyOf(elementData, newCapacity);

  23.    }


从源码可以看出,如果elementData==DEFAULTCAPACITYEMPTYELEMENTDATA,则就用默认容量10来进行开辟空间。这里的源码就解释了DEFAULTCAPACITYEMPTYELEMENTDATA数组和EMPTYELEMENTDATA数组的区别之所在。也给出了当我们用无参构造函数来实例化一个对象时,确实是构造的一个长度为10的数组对象。
grow() 函数的功能就是一个数组的扩张。一般情况下是扩展到原来数组长度的1.5倍。 但是,由于扩张1.5倍可能和我们的需要不一致,即可能太小,也可能太大。因此,就有了源码中的两个if条件的处理。即如果扩张1.5倍太小,则就用我们需要的空间大小minCapacity来进行扩张。如果扩张1.5倍太大或者是我们所需的空间大minCapacity太大,则进行Integer.MAXVALUE来进行扩张。

2.addAll(int index, Collection c)

ArrayList中元素的添加和删除操作都得依赖 System的arraycopy方法, System.arraycopy(a, 0, elementData, size, numNew);的意思是将数组a中从下标0开始,到0+numNew=numNew之间的元素移动到elementData数组中,但存放的位置是在size之后的

 
   
   
 
  1. public boolean addAll(int index, Collection<? extends E> c) {

  2.        rangeCheckForAdd(index);

  3.        Object[] a = c.toArray();

  4.        int numNew = a.length;

  5.        ensureCapacityInternal(size + numNew);  // Increments modCount

  6.        int numMoved = size - index;

  7.        if (numMoved > 0)

  8.            System.arraycopy(elementData, index, elementData, index + numNew,

  9.                             numMoved);

  10.        System.arraycopy(a, 0, elementData, index, numNew);

  11.        size += numNew;

  12.        return numNew != 0;

  13.    }

  • 将集合转化为对象数组

  • 判断容量及扩容

  • 如果需要移动 将elementData 从index的位置移动到index+numNew的位置

  • 将a中的元素复制到elementData空开的位置


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

JAVA常用集合源码解析系列-ArrayList源码解析(基于JDK8)

2JDK8中的HashMap实现原理及源码分析

JDK8 ArrayList源码分析

ArrayList 源码分析(未完待续)

java8 ArrayList源码阅读

源码解读:ArrayList源码解析(JDK8)