首页 > 技术文章 > ArrayList源码分析

51life 2018-07-06 09:53 原文

对于ArrayList,顾名思义,称之为数组列表。要明白,它的底层结构是数组,也就是说,ArrayList存储的元素其实是放在了它内部的一个数组中,对ArrayList的操作,其实就是对它内部这个数组的操作。接下来,我们从它的属性,构造器,方法三个方面来分析源码。

1.ArrayList的属性

     /**
     * Default initial capacity.
     */
//默认的初始容量 private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */
//一个空数组,当创建无参ArrayList时,底层数组默认的就是这个 private static final Object[] EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to * DEFAULT_CAPACITY when the first element is added. */
//存放元素的底层数组,此数组的长度也就是数组列表的容量 private transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */
//ArrayList的大小,也就是它存储元素的个数
private int size;

2.构造器

  /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
//构造一个ArrayList,并指定初始容量initialCapacity public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } /** * Constructs an empty list with an initial capacity of ten. */
//构造一个ArrayList,它初始容量是默认的,为10 public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */
//构造一个ArrayList,并将指定的集合中的元素放到这个数组列表中 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }

3.方法

add(E e) :将指定的元素添加到列表的尾部

add方法的逻辑如下:

(1).保证数组长度大于等于数组中的现有元素个数+1,这样才能添加下一个元素

(2).然后将修改次数+1,如果不能满足(1)中的条件,那么就需要进行扩容,创建一个新数组,其长度是原来数组的1.5倍,同时把原来数组中的元素都拷贝到新数组中。

 

public boolean add(E e) {
//确保ArrayList容量大于等于现有的元素个数+1 ensureCapacityInternal(size
+ 1); // Increments modCount!!
//将元素添加到数组的尾部 elementData[size++] = e; return true; }

 

 private void ensureCapacityInternal(int minCapacity) {
//获取期望得到的ArrayList容量
if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
  private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //修改次数+1

        // overflow-conscious code
//如果期望的到的ArrayList容量大现在的ArrayList容量,那么就扩容,就是创建一个长度更大的数组,然后将原来数组中的元素拷贝到新数组中,这个新数组作为ArrayList的底层数组
//需要理解的是:ArrayList的容量也就是它底层数组的长度,ArrayList的大小size是指它存储元素的个数;这两个概念要清楚 if (minCapacity - elementData.length > 0)
       //扩容操作   grow(minCapacity); }
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
//将原来数组中的元素拷贝到新数组中,新数组的长度是newCapacity elementData = Arrays.copyOf(elementData, newCapacity); }

add(int index,E element) : 将指定元素插入到此列表中的指定位置

由这个方法,我们可以明白对于ArrayList,插入的效率比较低的原因,因为从index位置开始的元素都整体往后一个单位进行拷贝。同理,删除操作效率较低也是类似的原因。

 public void add(int index, E element) {
//检查index,是否越界 rangeCheckForAdd(index); //确保数组长度够用 ensureCapacityInternal(size
+ 1); // Increments modCount!!
//通过copy的方式,从下标位置是index开始到最后一个元素,整体后移1个单位 System.arraycopy(elementData, index, elementData, index + 1, size - index);
//把指定元素插入index位置 elementData[index]
= element; size++; }

remove(int index):移除此列表中指定位置的元素

  public E remove(int index) {
//检查是否越界 rangeCheck(index); //修改次数+1 modCount
++;
//指定位置的元素 E oldValue
= elementData(index); //要移动的元素个数 int numMoved = size - index - 1; if (numMoved > 0)
//通过copy的方式,将index+1位置开始的元素整体向前移动一个单位(其实是拷贝) System.arraycopy(elementData, index
+1, elementData, index, numMoved);
//将最后一个元素位置的值设置为null,然后元素个数-1 elementData[
--size] = null; // clear to let GC do its work return oldValue; }

set(int index,E element):将指定元素替换此列表中指定位置的元素

 public E set(int index, E element) {
        rangeCheck(index);
        //获取指定位置的元素,被替换后,并返回
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

 remove(Object o):移除此列表中首次出现的元素

 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) {
//修改次数+1 modCount
++;
//需要移动的元素的数量
int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[
--size] = null; // clear to let GC do its work }

addAll(int index,Collection<? extends E> c):从指定的位置开始,将collection中的元素插入到此列表中

    public boolean addAll(int index, Collection<? extends E> c) {
        //判断index是否越界
rangeCheckForAdd(index); Object[] a
= c.toArray(); int numNew = a.length;
//确保数组长度够用,不够就扩容 ensureCapacityInternal(size
+ numNew); // Increments modCount //经过以下两次copy操作就可以把collection中的元素全部插入到列表中 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; }

总结:ArrayList的特点是查询效率高,增加和删除效率低,因为它的底层结构是数组。此外,它不是线程安全的。

推荐阅读