ArrayList (JDK8) 源码解析

作者: _Cancer | 来源:发表于2018-04-04 15:15 被阅读27次

    ArrayList 源码解析

    概述

    ArrayList 是一个动态数组,容量可以动态增长。他是线程不安全的,允许元素为null。它的底层数据结构是数组,所以会占用一块连续的内存空间。它实现了List<E>, RandomAccess, Cloneable, java.io.Serializable 接口。RandomAccess 代表List获取了随机访问功能,也就是通过下标获取元素对象的功能。

    构造函数分析

    定义

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

    构造函数

    执行完构造函数之后会构建出elementData数组和数量Size

    /**
     * Default initial capacity. 初始化容量
     */
    private static final int DEFAULT_CAPACITY = 10;
    
    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    /**
     * 默认构造函数中的空数组
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * transient 修饰表示elementData 不会被序列化, 因为elementData不是每次都是满的,每次都序列化会比较浪费时间
     */
    transient Object[] elementData; // non-private to simplify nested class access
    
    /**
     * The size of the ArrayList (the number of elements it contains).
     * 当前元素的数量
     * @serial
     */
    private int size;
    
    /**
     * 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
     */
    public ArrayList(int initialCapacity) {
       // 如果初始化容量大于 0 ,就新建一个长度为 initialCapacity 的Object 数组 这里没有修改Size(对比第三个构造)
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { //如果初始化容量为 0 ,直接将空的对象数组赋值给 elementData
            this.elementData = EMPTY_ELEMENTDATA;
        } else {// 容量小于 0 直接抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    
    /**
     * 默认构造方法,默认容量为10 ,
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
       //将空数组赋值给 elementData
        this.elementData = DEFAULTCAPACITY_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
     */
    public ArrayList(Collection<? extends E> c) {
       // 使用Collection.toArray()方法获取到一个对象数组,并复制给elementData
        elementData = c.toArray();
       // size 是集合当前的容量,所以通过别的集合来构造的时候要给size复制
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)//当c.toArray出错的时候没有返回对象数组,
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
    
    /**
     * 根据Class的类型去判断是去new一个对象数组还是去通过反射构造一个泛型数组,同时利用native函数,批量赋值元素至新数组中。 
     */
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        //利用native函数,批量赋值元素至新数组中
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
    

    常用API分析

    增加

    add(E e)

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e; //在数组末尾增加一个元素,并且修改size
        return true;
    }
    
    /**
     * Default initial capacity. 默认初始化容量
     */
    private static final int DEFAULT_CAPACITY = 10;
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//通过这个判断是否是使用默认构造函数初始化
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    
        ensureExplicitCapacity(minCapacity);
    }
    //这个方法是用来进行扩容检查的
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//如果确定要扩容,需要修改modCount  记录修改的次数
    
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)// 如果传过来的容量大小比初始长度,执行扩容方法
            grow(minCapacity);
    }
    /**
     * 真正的扩容方法,默认扩容一半
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
       //新的容量 = 老容量 + 老容量的一半, 右移一位表示除以 2分之1
        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:
       // 调用此方法进行扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of the class <tt>newType</tt>.
     * 
     * 
     */
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    

    add(int index, E element)

    添加元素 element 到 index 位置

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     * 添加元素到指定位置
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);//判断索引是否越界,若越界就会抛异常
        //扩容检查
        ensureCapacityInternal(size + 1);  // Increments modCount!!
       // 将指定下标空出,具体就是将index及其后的元素都往后挪动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
       // 赋值
        elementData[index] = element;
       // 长度加1
        size++;
    }
    

    addAll(Collection<? extends E> c)

    直接添加一个集合

    /**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the
     * specified collection's Iterator.  The behavior of this operation is
     * undefined if the specified collection is modified while the operation
     * is in progress.  (This implies that the behavior of this call is
     * undefined if the specified collection is this list, and this
     * list is nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
    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)

    添加集合到指定位置

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

    小结:先判断是否越界,是否需要扩容,如果扩容就需要复制数组,然后设置对应下标元素值。如果需要扩容,默认扩容一半,如果一半还不够,那么就用目标的size作为扩容后的容量。扩容成功后,会修改 modCount

    删除

    E remove(int index)

    按照下标删除

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     * 移除数组中指定位置的元素,后续元素都要左移,并且下标减一
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);//下标越界检查
    
        modCount++;//记录修改次数
       //获取要移除的元素
        E oldValue = elementData(index);
        //计算出需要左移的元素个数
        int numMoved = size - index - 1;
        //如果大于0说明有元素需要左移,如果等于0,说明移除的是最后一位
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);// index右侧的元素都左移一位,覆盖原来的元素
       //最后一个元素赋值为null,会被GC
        elementData[--size] = null; // clear to let GC do its work
    
        return oldValue;
    }
    

    boolean remove(Object o)

    移除指定元素

    public boolean remove(Object o) {
        if (o == null) { //如果元素为 null 遍历数组移除第一个null
         // 如果元素不为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 remove method that skips bounds checking and does not
     * return the value removed.
     */
    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
    }
    

    修改

    不会修改modCount,相对增删是高效的操作

    /**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        rangeCheck(index); //下标越界检查
       // 取出元素  
        E oldValue = elementData(index);
       // 覆盖元素
        elementData[index] = element;
        //返回老元素
        return oldValue;
    }
    

    查询

    不会修改modCount,相对增删是高效的操作

    E elementData(int index) {
        return (E) elementData[index];
    }
    
    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);
    
        return elementData(index);
    }
    

    清空

    modCount 会修改

    /**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     */
    public void clear() {
        modCount++;
    
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
    
        size = 0;
    }
    

    总结

    • 在增删改查中,增会导致扩容,会修改modCount,删也会修改,改和查一定不会修改
    • 扩容数组会导致复制数组,批量删除需要找到两个集合的交集然后复制数组操作,所以增和删比较低效,改和查比较高效。

    补充

    ArrayList 与 Vector 的区别

    • ArrayList是线程不安全的,Vector里面的方法都有synchronized修饰,所以是线程安全的
    • ArrayList扩容是扩充百分之五十,Vector扩容是翻倍的size。

    相关文章

      网友评论

      本文标题:ArrayList (JDK8) 源码解析

      本文链接:https://www.haomeiwen.com/subject/pvwkhftx.html