从源码分析java集合【ArrayList】

ArrayList的内部实现其实就是我们熟悉的数组,它继承了AbstractList,实现了List,RandomAccess,Cloneable和Serializable接口。

RandomAccess接口是说明实现类是支持快速随机访问的,它的随机访问的性能非常好,通常它的List的实现类:

 for (int i=0, n=list.size(); i < n; i++)
          list.get(i);
 
 //比下面这个循环更快

 for (Iterator i=list.iterator(); i.hasNext(); )
        i.next();

而LInkedList的sequentail access fast than random access,即顺序访问优于随机访问的。和LinkedList一样,它还实现了其他的接口。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>,RandomAccess,Cloneable,java.io.Serializable{
    /*
    * 默认的数组大小
    * */
    private static final int DEFAULT_CAPACITY = 10;

    /*
    * 用于空实例的共享空数组实例
    * */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /*
    * 用于默认大小实例的共享空数组实例,通过首次添加元素时会拓展多大来确定是调用它还是EMPRY_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 = {};

    /**
     *用来存储ArrayList元素的数组缓冲区,ArrayList的容量就是这个缓冲区的大小,
     *任何空的ArrayList都存在elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,
     * 当第一个元素添加时,它的容量会被扩展成DEAFAULT_CAPACITY.
     */
    transient Object[] elementData;

    /*
    * 包含元素的数量大小
    * */
    private int size;

    ...
}

与LinkedList一样,它从java.util.AbstractList继承了modCount,它们的作用是一样的,还有一点可以看出,ArrayList的默认capacity是10的。

关于EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别还是有点含糊,这里简单的说下两者在什么时候使用,当明确指出了capacity == 0时,就使用EMPTY_ELEMENTDATA,其余使用后者。我贴出源码可能就会很清楚了。

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

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

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

我们依旧先来看看ArrayList的add,remove,set和get的基本操作(限于本文篇幅,没有选形参不含index的方法)

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

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

    public E set(int index, E element) {
        rangeCheck(index);

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

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

        return elementData(index);
    }

可以看到,这些操作在判断完index是否可用后直接利用index对elementData进行操作,相对LinkedList先要进行node(index)而言,更加简洁,效率也更高。

但是有几个方法还是要说说。

<span style="white-space:pre">	</span>//看方法名就可以猜到,判断index能否直接进行add操作
        rangeCheckForAdd(index);

        //也可以猜到,就是用来复制数组的,在源码中能看到,大量使用它,其实Arrays.copyOf()其实就是对它的封装
        System.arraycopy();

        //需要好好说的是ensureCapacityInternal();
        ensureCapacityInternal();
        //它其实是一个系列的方法,用来确定ArrayList的capacity的大小。我们来看看源码,能够更清楚

        public void ensureCapacity(int minCapacity) {
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                    // any size if not default element table
                    ? 0
                    // larger than default for default empty table. It's already
                    // supposed to be at default size.
                    : DEFAULT_CAPACITY;

            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity);
            }
        }

        private void ensureCapacityInternal(int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }

            ensureExplicitCapacity(minCapacity);
        }

        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;

            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }

        //
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

    }
    //我觉得看代码已经很清楚了,总的来说是判断容量是否够用,假如够用而且大于DEFAULT_CAPACITY,
    // 则不做修改;假如小于DEFAULT_CAPACITY,则使用默认容量DEFAULT_CAPACITY;假如大于DEFAULT_CAPACITY
    //但是原来数组不够用,就重新开辟一个数组,大小为原来的1.5倍,并且将原来的值拷贝过去

还有一个比较好玩的方法时批量删除

private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
            //判断是否符合条件,符合则加入原数组,否则删除
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                        elementData, w,
                        size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }
    //注意当contains抛出异常时,会将之后的元素不加选择的全部添加到数组中,所以,批量删除并不能保证传入的集合中的元素被全部删除

在ArrayList中,调用iterator()会返回一个内部类Itr对象。它的定义如下:

private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
    
        //方法
        ...
}

看它的属性和后面的解释,就能知道他的意思了。因为每次他会对原来的数组进行再封装,而且我们随机访问的时间复杂度位O(1),所以在使用ArrayList时,根本没必要调用Iterator,调用它反而会增加开销。

在ArrayList还存在一个List,SubList,它能够截取this,具体看下面源码:

private class SubList extends AbstractList<E> implements RandomAccess {
        private final AbstractList<E> parent;
        private final int parentOffset;
        private final int offset;
        int size;

        SubList(AbstractList<E> parent,
            int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }
        //方法
        ...
    }

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

因为它实现了AbstractList,所以它也能完成List的方法。

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/u010233260/article/details/45276585
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞