ArrayList源码解析

ArrayList源码解析

全局变量说明
  • // 默认容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空数组,用于初始化时当初始化大小为0
    // 或者初始化时指定的集合大小为0时赋值给数组elementData
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 默认空数组,
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 存储元素的数组
    transient Object[] elementData;
    // 元素个数
    private int size;
    
三种集合初始化方法
  • 指定初始化容量

        public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                // 初始化list容量大于0时,创建大小为initialCapacity的数组
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                // 初始化list容量等于0时,elementData为空数组
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
  • 无参数直接进行初始化,elementData数组为默认空数组

        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
  • 指定初始化数据,参数为集合对象

        public ArrayList(Collection<? extends E> c) {
            // 指定的集合转为数组
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // 由于java8及之前的版本存在的bug会导致c.toArray()返回的类型可能不为Object[],
                // 这样就无法为数组添加多种不同类型的数据,因此通过数组复制的方式
                // 将其转化为Object[].
                // java9已解决c.toArray()返回结果不为Object[].class的问题.
                // 链接:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
                if (elementData.getClass() != Object[].class) {
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
                }                
            } else {
                // 指定的初始化数据大小为0时,elementData数组为空数组
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    
集合新增
  •     public boolean add(E e) {
            // 确保数据容量足够
            ensureCapacityInternal(size + 1);
            // 将当前元素添加到数组末尾
            elementData[size++] = e;
            return true;
        }
    
  •     // 计算存储数据所需要的最小容量
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            // 当数组为空数组时,由于存在addAll方法,可能会一次添加了大量元素,超过默认容量10
            // 因此所需最小容量取默认容量10和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) {
            //每次数组发生变化时+1,用于快速失败
            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;
            // 新的容量为原来数组容量的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            // 扩容后如果容量还是不够,则新的容量为之前计算的所需要的最小容量
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // 底层调用System.arraycopy进行元素的拷贝
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
        private static int hugeCapacity(int minCapacity) {
            // 当size+1的结果超过int所表示的最大值时,minCapacity会小于0
            if (minCapacity < 0)
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    
相关问题解释
  • 为什么要使用EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA两个空数组?
    • 使用两个空数组是为了区分两种不用的初始化方式,使用同一个空数组也是完全没有问题的
  • 变量elementData为什么要使用transient关键字?
    • 被transient修饰的对象不会进行序列化,elementData为数组容器,容器可能未被填满,如果将整个elementData数组进行序列会消耗更多的资源,ArrayList通过内部定义的writeObject和readObject进行序列化和反序列化,只对集合中的元素进行操作。
  • MAX_ARRAY_SIZE的作用?
    • 避免一些虚拟机内存溢出,-8是为了减少出错的几率,数组最大的长度依然是Integer.MAX_VALUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值