ArrayList与Vector扩容机制分析

1 ArrayList

1.1 类图

ArrayList与Vector扩容机制分析_第1张图片

如上图所示,是ArrayList的类图关系及全部字段。

1.2 增加元素

测试代码

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        list.add(i);
    }
}

通过这段测试代码,debug分析一下ArrayList增加元素以及底层扩容机制

调用无参构造器

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • elementData就是我们ArrayList底层存储数据的数组,是一个Object数组
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是ArrayList的一个字段,是一个空数组 {}

调用add()方法

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

size 是我们的ArrayList现有元素的数量,添加元素时,size++后调用ensureCapacityInternal()方法进行数组容量的的判断,初始时size=0

ensureCapacityInternal()方法

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

从参数和方法的名字(minCapacity)我们就可以看到这个方法是来确保我们添加元素时底层数组有足够的空间来容纳我们的元素,先来看calculateCapacity()方法

  • calculateCapacity(elementData, minCapacity)

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //首先进行判断,看elementData是否是{},就返回默认值和minCapacity的最大值
        //这里我们是第一次添加元素,前面在无参构造器中就已经将elementData赋为{}
        //所以这里我们的返回值就是默认的容量 10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //如果elementData不是默认的空数组就直接返回minCapacity(size + 1)
        return minCapacity;
    }
    
  • ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))

    private void ensureExplicitCapacity(int minCapacity) {
        //modCount是父类AbstractArrayList中的字段,记录的是我们对底层数组的修改(增删)次数
        modCount++;
    
        // 如果我们添加一个元素需要的最小容量比我们当前底层数组的大小要大时,这时我们就需要扩容
        // 调用我们的grow方法
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
  • grow(minCapacity)

    private void grow(int minCapacity) {
        // 记录当前数组的容量
        // 因为是第一次添加数据,这里oldC = 0;
        int oldCapacity = elementData.length;
        // 计算扩容后新数组应该具有的容量 由于是第一次添加元素,计算后newC = 0;
        // 这里我们就可以看到ArrayList底层数组每次按照当前数组大小的1.5倍进行扩容
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 0 - 10 < 0 所以进入下面的逻辑,直接将newC赋值为10
        // 所以我们的底层数组创建时是空数组,在第一次添加元素时进行初始化,初始化的大小是10
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 进行数组的扩容并将原有的数据copy下来
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    

至此,我们的ensureCapacityInternal(size + 1)方法结束,返回原add()方法中,直接在新的elementData的末尾添加上我们的新元素。

1.3 删除元素

这里通过以下代码debug以下删除元素的过程,测试代码如下:

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        list.add(i);
    }
    list.remove(5);
}

删除元素源码如下

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);
    //将最后一个元素置空并将size-1
    elementData[--size] = null; // clear to let GC do its work
	//返回删除的元素
    return oldValue;
}

1.4 修改元素

同过以下代码测试修改元素的方法set()

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        list.add(i);
    }
    list.set(1, 5);
}

添加元素源码如下:

public E set(int index, E element) {
    //检查传入的下标,防止下标越界
    rangeCheck(index);
	//记录修改前的值
    E oldValue = elementData(index);
    //修改index处元素的值
    elementData[index] = element;
    //返回修改前的元素
    return oldValue;
}

2 Vector

1.1 类图

ArrayList与Vector扩容机制分析_第2张图片

可以看到与ArrayList的类图结构是一样的

1.2 底层机理

Vector 和ArrayList一样,底层都是数组,不同的是Vector是线程安全的,通过源码我们可以看到Vector底层几乎所有的放法都加上了synchronized修饰

二者在增删改查上的机制差不多,在添加元素方法上有一些差别,现通过以下测试代码进行debug分析

public static void main(String[] args) {
    Vector<Integer> list = new Vector<>();
    for (int i = 0; i < 20; i++) {
        list.add(i);
    }
}

首先调用构造方法

public Vector() {
    this(10);
}

这里就和我们的ArrayList不一样的,我们的ArrayList在调用无参构造器时,赋给底层数组的是一个空数组,而Vector在调用无参构造器时已经给了底层数组一个容量——10

调用add()方法

这里的流程和ArrayList的流程基本一致,不过在数组容量不足,给数组扩容的grow()方法有些许差别,如下所示

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 主要就是这里不同,我们的Vector是按照当前数组容量的两倍进行扩容,而ArrayList是1.5倍
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

你可能感兴趣的:(java,后端)