程序员

ArrayList源码分析

2017-11-23  本文已影响21人  SeaRise

整体介绍

ArrayList实现了List接口,是一个常见的集合类,它有一下特点:

摊还时间是一个操作的平均代价,可能某次操作代价很高,但总体的来看也并非那么糟糕;add方法是摊还常量时间与底层数组容量自动扩充有关

image.png

源码分析

成员变量

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

    /**
     * 当ArrayList的空实例,用于无参数初始化
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * ArrayList的底层数组
     * 泛型只是编译器提供的语法糖所以这里的数组是一个Object数组
     * ArrayList的容量就是elementData.length
     * 当ArrayList为空时,elementData == EMPTY_ELEMENTDATA
     * 当第一个元素加入时elementData.length == DEFAULT_CAPACITY
     * transient 说明这个数组无法序列化。
     */
    private transient Object[] elementData;

    /**
     * ArrayList包含的元素数量,与容量不同.
     */
    private int size;

    /**
     * 数组最大容量
     * 一些虚拟器需要在数组前加个头标签,所以减去8 。 
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 这个变量并不是ArrayList里面定义的,而是继承自父类AbstractList
     * ArrayList在使用Iterator时发生不同步会抛出错误就是依靠了这个变量
    (虽然不可靠,想要可靠的同步保证用
    List list = Collections.synchronizedList(new ArrayList(...))
    或
    concurrent包下的CopyOnWriteArrayList
    或
    用synchronized进行一层代理)
     * add和remove方法都是有modCount++.
     * 下面再具体说说modCount的作用
     */
     protected transient int modCount = 0;

get()

get()方法本身很简单,不涉及数组的容量扩充,除了检查下标范围以及类型转换,与一般的数组get()操作没区别

   public E get(int index) {
        //检查是否越界,
        //由于java数组本身就会检查是否越界,所以这里只是检查是否超过了size,
        //要知道数组的大小大于size,index大于size并不会触发数组越界.
        rangeCheck(index);
        return elementData(index);
    }
    
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        //对元素进行类型转换,底层储存是Object,但返回是E.
        return (E) elementData[index];
    }

set()

set()方法也很简单,不涉及数组的容量扩充,除了检查下标范围,与一般的数组set()操作没区别

    public E set(int index, E element) {
        //在get()中提到的,检查越界问题.
        rangeCheck(index);
        //直接用element替换elementData[index]
        //赋值到指定位置,复制的仅仅是引用
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

add()

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    /**
     *从这个方法可知第一次add元素,初始容量为DEFAULT_CAPACITY,即10
     */
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    /**
     *add()的modCount++来源于这里
     *当新容量大于当前容量时,触发容量扩充,即调用grow方法.
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    /**
     *ArrayList实现容量自动扩充是依靠了这个方法.
     *新容量为原容量的1.5倍
     *耗费时间为O(n),因为需要复制原来的元素到扩充后的数组.
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //每次扩充,新容量为原容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // //扩展空间并复制
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

remove

    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]的引用,
       //因为假如不清除引用,那么ArrayList就会一致保留那个对象
       //GC不能清除仍被持有引用的对象
        elementData[--size] = null;

        return oldValue;
    }

序列化

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // 记下方法开始时的modCount
        int expectedModCount = modCount;
        //将没有transient关键字的成员变量写入
        s.defaultWriteObject();

        //这里写入数组的容量,但是把数组元素的数量size视为容量,
        //而不用原来的容量elementData.length
        s.writeInt(size);

        // 将底层数组的元素依次写入
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        //与之前记下的modCount对比,看是否有其他线程操作了ArrayList,有则抛出错误.
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // 将没有transient关键字的成员变量读入
        s.defaultReadObject();

        // 将数组容量读入,
        //但是如上面writeObject所写把size视为了数组容量,所以这里没有实质的作用
        s.readInt(); // ignored

        if (size > 0) {
            // 扩充数组大小
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // 把数组元素一次写入
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

迭代器

ArrayList用iterator()可以返回迭代器.

iterator()
    public Iterator<E> iterator() {
        //使用构造函数,返回一个新的迭代器
        return new Itr();
    }
迭代器成员变量
        int cursor;       // 下一个返回的元素的下标
        int lastRet = -1; // 上一次返回的元素的下标,初始为-1
        //记下创建迭代器时的modCount,用于之后的同步检查
        int expectedModCount = modCount;
hasNext()

hasNext()用于检测是否有下一个元素返回,实现很简单.

        public boolean hasNext() {
            return cursor != size;
        }
checkForComodification()
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
next()
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
remove()
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

后记

上一篇 下一篇

猜你喜欢

热点阅读