从源码分析java集合【ArrayList】

来源:互联网 发布:dnf网络波动 编辑:程序博客网 时间:2024/05/11 18:05

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的方法。




0 0
原创粉丝点击