ArrayList源码解读(JDK1.7 与JDK1.8)

1,309 阅读4分钟

一直非常想写这一篇解读,奈何自己水平不高,看了很多资料,才了解清楚,本篇文章主要是对比1.7和1.8在添加元素时的不同,对删除,更新不做详细介绍,文章最后会给出两篇优秀的文章来详细介绍jdk1.8。

JDK1.8解读

先来看一下ArrayList的继承关系图谱


通过图谱可以观察出来ArrayList是继承了AbstractList,进一步对AarryList解读:可以看到ArrayList实现了List、RandomAccess 、Cloneable 、Serializable接口等。


实现List:说明ArrayList是存取有序,可以有重复元素,且元素可以是为null值;

实现RandomAccess:说明支持随机快速访问,但是该接口内并没定义任何逻辑,ArrayList底层数据结构是数组,所以可以快速访问;


实现Cloneable:说明支持拷贝,且是浅拷贝;

实现Serializable:说明可以实现序列化。

全局变量



构造函数


对构造函数进一步解读:

     //默认构造函数
       public ArrayList() {
     //创建空列表,并不像1.7创建对象就实例化,更像一个懒加载
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    //指定初始容量参数的构造函数
    public ArrayList(int initialCapacity) {
    // 容量大于0
        if (initialCapacity > 0) {
            //创建指定大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //创建容量为10空数组,
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
           // 报错
            throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
        }
    }
         //构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
      public ArrayList(Collection<? extends E> coll) {
        elementData = coll.toArray();
        //如果指定集合元素个数不为0
        if ((size = elementData.length) != 0) {
            // coll.toArray 可能返回的不是Object类型的数组所以加上下面的语句用于判断,
            //使用反射里面的getClass()得到elementData 所在类
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
            // 用空数组代替
            this.elementData = EMPTY_ELEMENTDATA;
            }
        }

增加元素到ArrayList


添加元素,调用ensureCapacityInternal方法


ensureCapacityInternal调用ensureExplicitCapacity,再调用grow方法


//        private int size;私有化一个int型变量
//        判断是否添加元素
        public boolean add(E e) {
//            判断容量
            ensureCapacityInternal(size + 1);
//            数组添加元素,size是下标
            elementData[size++] = e;
            return true;
        }

        public void add(int index, E element) {
//            检查索引
            rangeCheckForAdd(index);
            ensureCapacityInternal(size + 1);
            System.arraycopy(elementData, index, elementData, index + 1,
                    size - index);
            elementData[index] = element;
            size++;
        }
        private void rangeCheckForAdd(int index) {
            if (index > size || index < 0)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
        public void ensureCapacity(int minCapacity) {
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;

            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity);
            }
        }
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
        private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        private void grow(int minCapacity) {
//            得到原先数组的长度
            int oldCapacity = elementData.length;
//            新的数组长度等于原先数组长度加上原先数组长度的一半,oldCapacity右移一位相当于1/2 oldCapacity
            int newCapacity = oldCapacity + (oldCapacity >> 1);
//            如果新数组长度减去数组元素个数小于0,则把数组元素个数作为新数组的长度
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
//            如果新数组长度减去数组最大的定义长度,则调用hugeCapacity方法
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
//            元素数据等于,数组复制元素以及容量
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        private static int hugeCapacity(int minCapacity) {
//            小于0抛出异常
            if (minCapacity < 0)
                throw new OutOfMemoryError();
//            如果元素大于数组最大长度,返回整数的最大值,不然返回数组最大长度
            return (minCapacity > MAX_ARRAY_SIZE) ?
                    Integer.MAX_VALUE :
                    MAX_ARRAY_SIZE;
        }

在没有设置数组初始大小时,默认数组大小为0,例如:

ArrayList list =new ArrayList();//创建长度为object[] elementData初始化为{},并不是长度为10
list.add(1);//当第一次add时候,创建10长度的数组。向数组中添加元素 elementData[0] = new Integer(1)
....
list.add(11);//本次添加,数组容量不够,触发扩容机制,扩容后的数组长度为原来数组的1.5倍,并将原来数组中的元素复制到新数组中

添加元素的思路:

首先判断新增一个元素是否会超过数组大小,如果数组是一个空数组,添加元素就初始化一个长度为10 的数组;再次添加,直到数组溢出,溢出就扩容,每一次扩容是原来数组长度的1.5倍;最后使用Arrays.copyOf()方法将原来数组中的元素复制到新的数组中(此处为浅拷贝)。

推荐开发中尽量使用带参构造器,减少触发扩容机制,提高效率。

JDK1.7解读

看一下ArrayList的继承关系图谱


构造函数



添加元素的思路:

首先先创建一个长度为10的数组,接着判断新增一个元素是否会超过数组大小,如果不超过就直接添加数组长度缩小;再次添加,直到数组溢出,溢出就扩容,每一次扩容是原来数组长度的1.5倍;最后使用Arrays.copyOf()方法将原来数组中的元素复制到新的数组中(此处为浅拷贝)。

总结:jdk 1.7 中ArrayList的对象创建类似于单列的饿汉模式,一来就先创建好一个大小为10的数组,而jdk1.8中ArrayList的对象创建类似于单列的懒汉模式,只有添加数据才创建大小为10的数组,延迟了数组的创建,节省内存空间。

参考1:github.com/wupeixuan/J…

参考2:blog.csdn.net/xfhy

特别注意:本文部分摘抄文字版权属于原作者!!!