Analysis of ArrayList principle

Ask questions

  1. Based on what? Array? Linked list? queue?
  2. Why can you keep the addelement?

analysis

Method to realize

Variables defined:

An array is maintained:

transient Object[] elementData; // non-private to simplify nested class access

private int size;

ArrayListEverything internally operates add、remove、set、geton elementDatathis array, so the ArrayListimplementation is based on the array.

Two lengths: size====>The length of the current list elementData.length====> Array length
Array length ≥ List length

The default length and two default arrays:

/**
 * 默认的数组长度,当我们直接创建一个ArrayList对象时,容量为10
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 直接创建无参ArrayList时,内部指向该数组
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 创建带初始长度的ArrayList,或者传入另一个列表为参数创建对象时,如果长度为0或者传入列表长度为0,内部指向该数组
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

Construction method

ArrayListThree construction methods are defined. They are non-parameter construction. One inttype parameter construction method is passed in, and a list is passed as the parameter construction method.

  1. Pass in the initial length. This method is generally used when we know the length of the list to avoid applying too much useless memory space
public ArrayList(int initialCapacity) {
    //如果长度大于0,则创建该长度的数组
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    //长度为0,指向默认数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
  1. No parameter construction, will elementDatapoint to the default array, length 0
  2. Pass in a list. Arrays.copyOf()Copy the passed list to the new array by method, and then elementDatapoint to the address

Operation method

The internal implementation of each method is an operation on the array

get

Get the value of the corresponding subscript of the array directly

public E get(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    return (E) elementData[index];
}

set

The same operation on the array, replace the value corresponding to the subscript in the array with the new value

public E set(int index, E element) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    E oldValue = (E) elementData[index];
    elementData[index] = element;
    return oldValue;
}

add

The subscript is current size+1, and the value that needs to be added is set to the value corresponding to the subscript in the array. Because the length of the array is fixed, it involves the most important expansion and growth strategy, ensureCapacityInternal(size + 1)method

public boolean add(E e) {
    //扩容算法,传入当前长度+1
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //扩容完成后赋值
    elementData[size++] = e;
    return true;
}

addAll

Method, first convert the list parameter to an array, and then use the System.arraycopy method to copy the array to elementDatait, which also involves the expansion and growth strategy, but the incoming parameters are different .

public boolean addAll(Collection<? extends E> c) {
    //先转为数组
    Object[] a = c.toArray();
    int numNew = a.length;
    //扩容算法,传入当前长度+需要add的元素的数量
    ensureCapacityInternal(size + numNew);  // Increments modCount
    //拷贝数组
    System.arraycopy(a, 0, elementData, size, numNew);
    //列表长度修改
    size += numNew;
    return numNew != 0;
}

remove

The same copy operation is performed on the array, which is similar to moving each element forward one bit after the element that needs to be removed in the array, overwriting the original value.

public E remove(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    modCount++;
    E oldValue = (E) 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;
}

Expansion

Front addand addAllmethods, there is ensureCapacityInternal1a method for expansion algorithms
look at the relevant code expansion:

//①传入列表长度值
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}
//②如果计算后需要的长度大于当前数组的长度,执行扩容操作
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

//③对数组进行扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    //获取当前长度
    int oldCapacity = elementData.length;
    //计算新长度为旧长度 * 1.5 倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果新长度小于需要的长度,使用传入的长度,适用于第一次创建后添加元素的扩容和addAll方法元素很多超过原有1.5倍的情况下
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //如果计算后长度大于最大列表长度
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //通过copyOf方法将原有数据拷贝到一个新的数组对象中,再赋值给elementData,至此,扩容完成。
    elementData = Arrays.copyOf(elementData, newCapacity);
}

analysis:

  1. First we pass in a value, which represents the length of our list after add or addAll
  2. You cannot expand the array every time you add , otherwise the cost will be too great, so the expansion of the array will not only increase the length by one each time.
  3. If it is currently an empty array, take the maximum of the default value and the passed value. In order to create an array of length 10 directly for the first time, otherwise add an element to expand once, which is expensive
  4. Calculate whether expansion is needed
  5. Expansion operation, under normal circumstances, the array capacity is expanded by 1.5 times , two special cases:
    1. Add element for the first time, directly expand to the default length of 10
    2. There are a lot of addAll elements, and the total length is more than 1.5 times the original length, directly expand to this length
  6. Copy all the original data to the expanded new array through the array copy method, and finally assign the array to elementData
  7. Expansion completed

to sum up

  1. ArrayList is implemented based on an array, the default length of the array is 10, all operations are operations on the maintained array
  2. The add and addAll methods may trigger array expansion
  3. When the length is not enough, the expansion length is generally 1.5 times the existing length
  4. Expansion, deletion, etc. are all achieved by copying the array, so the list should not be too long, otherwise each copy will consume too much performance!

Guess you like

Origin blog.csdn.net/lizebin_bin/article/details/88876878