Java集合框架源码——ArrayList解析

ArrayList 是 Java 中很常用的集合之一。它基于数组实现了一个可变长度的集合。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

    List list = Collections.synchronizedList(new ArrayList(...)); 

四个私有属性

    // 默认初始容量
    private static final int DEFAULT_CAPACITY = 10;

    // 空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 当调用无参构造函数时使用的空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // 真正的保存数据的数组 transient关键字说明其不能被序列化
    // 不能序列化的原因是,数组内存储的的元素其实只是一个引用,单单序列化一个引用没有任何意义,反序列化后这些引用都无法在指向原来的对象。
    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);
        }
    }

    // 无参的构造函数,新建的数组为空数组,在第一次添加元素时大小扩展为10
     public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }  

    // 构造方法传入一个Collection, 则将Collection里面的值copy到ArrayList
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

成员函数

容量控制

trimToSize

将 ArrayList 的容量设置为当前 size 的大小。由于是由数组来实现的,ArrayList 申请的空间大小是要比实际元素占用的空间要大一些,此函数去掉 null 值的空间。
modCount 是从 AbstractList 中继承的变量,当集合的结构发生改变时,它会自增在原值上加一,这样可以保证在多线程的环境下,如果一个线程对 List 进行了结构上的改变时,modCount 自增,另一个线程在迭代的时候,由于每一次例如 next、remove、previous、set 或 add 操作方法都会检查原 expectedModCount 值和当前 modCount 值是否相等,此时显然两者不等,就会报错 ConcurrentModificationException 。

    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

ensureCapacity

ArrayList 在自动扩容时,每次数组容量的增长大约是其原容量的1.5倍。会重新建立这个约1.5倍大小的新数组,然后将老数组的数据拷贝到新数组中去,所以,数组的频繁扩张会造成很大的开销。因此,在初始化时,尽量估算容量需求,尽量避免数组扩容;或者通过调用 ensureCapacity 方法来手动增加容量。

    public void ensureCapacity(int minCapacity) {
        // 判断当前数组是否为空数组,如果不为空,最小增长空间为0;如果为空,那么最少需要10的空间
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            ? 0
            : DEFAULT_CAPACITY;
        // 如果所需空间大于最小增长空间,那么按所需空间来增长
        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    private void ensureExplicitCapacity(int minCapacity) {
        // 快速失败变量 modCount 值在List结构变化时都要自增加一
        modCount++;
        // 所需空间大于elementData数组当前的空间时,才需要增长
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    // 数组的最大容量定义为最大整数少一个字节,(虚拟机会存入一些数组的信息,减少一个字节,更大程度的避免内存溢出)
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private void grow(int minCapacity) {
        // 获取当前elementData数组的长度
        int oldCapacity = elementData.length;
        // 新长度扩容至1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 判断新长度和所需长度的大小,如果1.5倍还小,那就使用所需的长度;如果1.5倍可以了,那就使用1.5倍的大小
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 判断新长度有没有超过MAX_ARRAY_SIZE,如果超过了,进入hugeCapacity函数处理,那么看所需长度是否超过了MAX_ARRAY_SIZE,如果所需长度仍超出,那么新长度就设为Integer.MAX_VALUE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间,并将elementData的数据复制到新的内存空间
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

元素添加

set(int index, E element)

在指定位置添加新元素,返回老元素

    public E set(int index, E element) {
        // 越界就抛越界异常
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

add(E e)

在 ArrayList 尾部添加元素时,会调用 ensureCapacityInternal 这个方法来扩张数组。(ensureCapacity 是 public 的,可以手动调用,这个 ensureCapacityInternal 是添加时调用的 private 函数)

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 判断当前数组是否为空,如果为空则返回默认容量10和需求容量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 值在List结构变化时都要自增加一
        modCount++;

        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

add(int index, E element)

调用了 System.arraycopy 方法来实现数组的复制。其实就是将数组指定位置之后的后移一位,然后将新元素插入到指定位置上。

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

addAll(Collection<? extends E> c)

将该collection中的所有元素添加到此列表的尾部。

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

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

从指定的位置开始,将指定collection中的所有元素插入到此列表中。

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

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        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;
    }

元素删除

remove(int index)

检查范围,modCount 自增,然后保存指定位置的旧元素,执行数组拷贝,然后返回旧元素。(最后位置的元素由于已经没有意义,置为 null)

    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; // clear to let GC do its work

        return oldValue;
    }

remove(Object o)

通过遍历数组来寻找待删除的对象,一旦找到第一个,就执行快速删除(类似 remove,但不判断越界,不返回旧元素)

    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; // clear to let GC do its work
    }

removeRange(int fromIndex, int toIndex)

给定起始和终止位置,其中的元素全部被移除。(和 String 的 substring 的起始和终止类似,toIndex 位置的那个元素不删除)

    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

clear()

数组中元素全部置为 null ,等 GC 回收

    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

元素读取

get(int index)

返回指定位置上的元素

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

转为静态数组

toArray()

调用 Arrays.copyOf 方法,返回一个新数组,新数组拷贝了 elementData 从 0 至 size-1 位置的元素共 size 个并返回。

    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

toArray(T[] a)

传入数组的长度小于 size,将返回一个 size 长度的数组。如果传入数组长度大于 size,那么后边多出的位置都置为 null 。

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值