gaarakseven

导航

Collection之ArrayList 源码解读

Collection之ArrayList

环境

基于 jdk 1.8

new ArrayList() 实例化过程以及新增元素过程

ArrayList使用一个transient关键字修饰的 数组 elementData 存储数据。
实例化ArrayList时不指定长度的话(调用空构造器), elementData会被赋值为一个长度为0的 ArrayList 内定义的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA

public ArrayList() {    
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

而后,在第一次调用add方法添加元素时,会将ArrayList实例的elementData数组重新赋值成一个默认长度为10的、所有元素都为空的数组,然后将elementData的第 size (size代表ArrayList实际容纳的元素个数, 为int类型,故一开始为0)个元素赋值为新增的元素,复制后size++

public boolean add(E e) {    
    //对 elementData 的初始化、重新赋值都在这个方法里进行; 因为新增一个元素, 故传入值 size+1 ;在 addAll(Collection.class) 方法中,会传入值 (size + Collecttion.class.size())
    ensureCapacityInternal(size + 1);  // Increments modCount!!    
    elementData[size++] = e;    
    return true;
}

进入 ensureCapacityInternal(size + 1) 方法查看初始化、重新赋值的过程

//minCapacity代表新增元素后list实际容纳元素个数
private void ensureCapacityInternal(int minCapacity) {
    // 这个方法会判断新增元素后的实际容纳元素个数是否大于 elementData.length, 大于时会进行elementData的扩容
    ensureExplicitCapacity(
        //当 elementData 等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时(即 ArrayList 被实例化但未新增元素), 返回默认数组长度值(10)和 minCapacity 中的最大值; 这个逻辑只会在初次为 ArrayList 实例添加元素时走一次, 因为初次添加元素时会重新生成一个数组对象并赋值为 elementData, 此时 elementData 就不再等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 后续再新增元素时, 否则直接返回 minCapacity。详见 ensureExplicitCapacity(int) 方法
        calculateCapacity(elementData,minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {    
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {        
        return Math.max(DEFAULT_CAPACITY, minCapacity);    
    }    
    return minCapacity;
}


private void ensureExplicitCapacity(int minCapacity) {    
    modCount++;    // overflow-conscious code    
    //minCapacity大于elementData.length就意味着为ArrayList新增元素后的实际元素容纳个数已经超出elementData的范围,需要扩容
    if (minCapacity - elementData.length > 0)
        //将elementData的元素按序拷贝进一个容量更大的数组,将该数组赋值给elementData
        grow(minCapacity);
}

再看一下grow(minCapacity)方法是如何扩容的

/** * Increases the capacity to ensure that it can hold at least the 
* number of elements specified by the minimum capacity argument. 
* 
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {    // overflow-conscious code    
    int oldCapacity = elementData.length;
    //将新容量预设为旧容量的1.5倍; 这一步可能会有溢出的情况, 即 newCapacity的理想值 超过 Integer 的最大值, 导致 newCapacity 变成负数
    int newCapacity = oldCapacity + (oldCapacity >> 1);    
    //这一步是解决空构造器实例化ArrayList时,elementData.length=0的问题
    //或者, 解决溢出的问题
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;    
    //此时 newCapacity 要么等于1.5倍的 oldCapacity, 要么等于 minCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //这一步要么抛溢出异常, 要么返回 ArrayList允许的最大值 MAX_ARRAY_SIZE(当 minCapcacity > MAX_ARRAY_SIZE), 要么返回 Integer 的最大值(当 minCapcacity < MAX_ARRAY_SIZE)
        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;
}

ArrayList 通过空构造器初始化过程以及新增元素的过程如上.

删除元素逻辑 remove()

检查要删除的元素位置是否超过list的元数素个数, 并将elementData的第 (index+1)个元素及以后的元素依次赋值给其前一个元素, 然后将最后一个元素赋值为null, 让gc准备回收

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

还有一个 remove(Object) 方法, 判断list是否存在跟Object相等的值, 删除逻辑类似

posted on 2021-02-08 20:43  gaarakseven  阅读(63)  评论(0编辑  收藏  举报