ArrayList源码详解(JDK1.8版)

一. 概述

        ArrayList是一个非线程安全的有序集合, 按照添加顺序排列, 能够随机添加或删除元素, 支持任意类型元素, 支持泛型。java

二. JDK版本差别

      对于ArrayList,  JDK1.8相对于JDK1.9没有什么大变化,  JDK1.8版本比JDK1.7版增长了一些函数编程特性方法, 以下编程

//循环每一个元素, 以元素为参数执行action操做
    @Override
    public void forEach(Consumer<? super E> action) {
        ...
    }
     
    //经过Predicate(断言函数接口)实现过滤删除
    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        ....

        return anyToRemove;
    }
    

    //经过UnaryOperator(一元操做函数接口)来实现替换    
    @Override
    @SuppressWarnings("unchecked")
    public void replaceAll(UnaryOperator<E> operator) {
        ...
    }

      

三. 数据结构     

/**
     * 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 Object[] elementData; // non-private to simplify nested class access

      从以上源码能够看出:数组

  • ArrayList使用一个Object数组存储元素, 说明是有序的
  • elementData 声明为transient,  不参与序列化
  • 非私有实例变量, 容许内部类访问, ArrayList存在一些内部类须要操做elementData,                       如: ListItr,SubList,ArrayListSpliterator。

   

四. 实例初始化

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

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    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实例化有如下三种:安全

  • public ArrayList(int initialCapacity): 指定容量的初始化. 若是initialCapacity>0, 则建立一个大小为initialCapacity的数组,若是initialCapacity=0, 则建立一个空数组(EMPTY_ELEMENTDATA), 若是小于0, 则抛出异常.
  • public ArrayList(): 建立一个空数组
  • public ArrayList(Collection<? extends E> c): 经过外部集合初始化.将集合c转为数组, 赋给本身数组elementData, 若是数组类型不是 Object[], 则经过Arrays.copyOf()转换。

五. 扩容机制

private void grow(int minCapacity) {
        //旧容量
        int oldCapacity = elementData.length;
        //计算新容量
        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);
    }
    //设置为大容量
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

扩容经过grow()方法实现, 主要作了如下三项工做:数据结构

  •  计算ArrayList新的容量.                                                                                                                  int newCapacity = oldCapacity + (oldCapacity >> 1)
      oldCapacity >> 1 :  移位运行, 至关于除2,  表明oldCapacity(原容量)的1/2
      也就是说ArrayList扩容大小为原容量的二分之一.
  • 若是newCapacity(扩容后的容量)大于MAX_ARRAY_SIZE(容量上限), 则设置为Int类型的最大值
  • 使用Arrays.copyOf()方法复制一个新容量的数组

六. 元素添加

/**
     * 添加一个元素, 支持任何类型
     *
     */
    public boolean add(E e) {
        //确保内部容量能够支持添加的元素
        ensureCapacityInternal(size + 1);
        //向数组中添加元素
        elementData[size++] = e;
        return true;
    }

    /**
     * 将元素添加到特定的索引位置
     *
     * @param index 待添加元素的索引
     * @param element 待添加元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        //检查index是否超出数组的index限制,若是超过, 
        //则抛出数组越界异常(IndexOutOfBoundsException)
        rangeCheckForAdd(index);

        //确保内部容量能够支持添加的元素
        ensureCapacityInternal(size + 1); 
        //复制一个新数组, 目的是为了移位,将插入点index以后的元素日后移动
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
 
    //确保内部容量能够支持添加的元素
    private void ensureCapacityInternal(int minCapacity) {
        //若是数组为空, 多是单元素添加或批量元素添加触发当前操做
        //单元素添加: minCapacity = DEFAULT_CAPACITY(至关于初始化默认容量)
        //批量元素添加: minCapacity不变
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //确保准确的容量
        ensureExplicitCapacity(minCapacity);
    }

    //确保准确的容量
    private void ensureExplicitCapacity(int minCapacity) {
        //解决并发问题的,后续会提到
        modCount++;

        // elementData新增元素后, 若是当前容量不能知足elementData的长度, 则进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  •  ArrayList添加元素时, 会先检查容量, 容量不够,则扩容,  扩容会阻塞元素添加.
  • 按index插入元素会触发元素移位, 这样会影响性能, 插入位置越靠前, 影响越大
  • ArrayList经过System.arraycopy()方法实现的移位, 这个方法能够将A数组的某个索引后的元素复制到B数组里, 组成一个新的数组

七. 元素删除

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 boolean remove(Object o) {
        if (o == null) {
            //查找为null的元素, 并删除
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            //查找不为null的元素,并删除
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * 快速删除, 不校验index溢出, 不返还删除元素对象
     */
    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
    }
  •     ArrayList删除元素时, 一样须要移动元素,不过是向前移动, 数据量大时, 影响性能, 删除位置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);
    }
  •  元素获取比较简单,  先校验index是否数组越界, 而后经过index直接从elementData数组中获取元素,说明ArrayList的获取操做效率很高

九. 线程安全

     ArrayList的 add, remove,get操做都没有加锁, 因此是非线程安全的。并发

十. modCount是干什么用的?

protected transient int modCount = 0;

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

 这个变量充斥着整个ArrayList类中, 凡有修改的地方就有他它,   它记录着ArrayList元素修改次数。ide

下面来讲明它的用处:函数

//迭代器
     public ListIterator<E> listIterator(final int index) {
            //检查修改状态(modCount)
            checkForComodification();
            rangeCheckForAdd(index);
            final int offset = this.offset;

            return new ListIterator<E>() {

                @SuppressWarnings("unchecked")
                public E next() {
                    checkForComodification();
                    ......
                }

                public void remove() {
                    if (lastRet < 0)
                        throw new IllegalStateException();
                    checkForComodification();

                    try {
                        ......
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }

                public void add(E e) {
                    checkForComodification();

                    try {
                        ......
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }

                final void checkForComodification() {
                    //若是不相等, 说明其余线程修改了ArrayList元素
                    if (expectedModCount != ArrayList.this.modCount)
                        throw new ConcurrentModificationException();
                }
                
               ......
            };
        }

        能够看出在ListIterator迭代器中, 进行任何操做都会调用checkForComodification() 检查modCount是否有变更, 若是有变更, 就表明其余线程修改了ArrayList的元素,而后抛出ConcurrentModificationException异常.性能

     它的做用是用于实现快速失败机制(fail-fast), 当咱们对ArrayList的元素迭代时, 若是其它线程修改了某个元素, 会产生迭代结果会不正确或迭代失败, 快速失败机制能够提早检测迭代失败问题, 避免浪费时间, 影响性能。ui

十一. 总结

  •  ArrayList是一个非线程安全的有序集合.
  •  ArrayList在插入(非末尾插入)元素时, 须要向后移动内部元素, 添加位置越靠前, 性能影响越大.
  •  ArrayList在删除(非末尾删除)元素时, 须要向前移动内部元素, 删除位置越靠前, 性能影响越大.
  •  ArrayList采用动态扩容机制, 每次扩容原容量的一半, 也就是1.5倍. 最大为Integer的最大值.
  •  ArrayList 经过Arrays.copyOf()方法实现的扩容
  •  ArrayList 经过System.arraycopy()方法实现元素的移位
  •  ArrayList 经过modCount实现了fail-fast机制
相关文章
相关标签/搜索