Análisis del principio ArrayList

Hacer preguntas

  1. ¿Basado en que? ¿Formación? ¿Lista enlazada? ¿cola?
  2. ¿Por qué puedes quedarte con el addelemento?

análisis

Método para realizar

Variables definidas:

Se mantiene una matriz:

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

private int size;

ArrayListTodo opera internamente add、remove、set、geten elementDataesta matriz, por lo que la ArrayListimplementación se basa en la matriz.

Dos longitudes: size====> La longitud de la lista actual elementData.length====> Longitud de la
matriz Longitud de la matriz ≥ Longitud de la lista

La longitud predeterminada y dos matrices predeterminadas:

/**
 * 默认的数组长度,当我们直接创建一个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 = {};

Método de construcción

ArrayListSe definen tres métodos de construcción. No son de construcción de parámetros. Se intpasa un método de construcción de parámetro de tipo y se pasa una lista como método de construcción de parámetros.

  1. Pase la longitud inicial. Este método se utiliza generalmente cuando conocemos la longitud de la lista para evitar aplicar demasiado espacio de memoria inútil.
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. Sin construcción de parámetros, apuntará elementDataa la matriz predeterminada, longitud 0
  2. Pase una lista. Arrays.copyOf()Copie la lista pasada a la nueva matriz por método y luego elementDataapunte a la dirección

Método de operación

La implementación interna de cada método es una operación en la matriz.

get

Obtenga el valor del subíndice correspondiente de la matriz directamente

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

    return (E) elementData[index];
}

set

La misma operación en la matriz, reemplace el valor correspondiente al subíndice en la matriz con el nuevo valor

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

El subíndice es actual size+1y el valor que debe agregarse se establece en el valor correspondiente al subíndice en la matriz. Debido a que la longitud de la matriz es fija, implica la estrategia de expansión y crecimiento más importante, el ensureCapacityInternal(size + 1)método

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

addAll

Método, primero convierta el parámetro de lista en una matriz y luego use el método System.arraycopy para copiar la matriz en elementDataella, lo que también implica la estrategia de expansión y crecimiento, pero los parámetros entrantes son diferentes .

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

La misma operación de copia se realiza en la matriz, que es similar a mover cada elemento hacia adelante un bit después del elemento que debe eliminarse en la matriz, sobrescribiendo el valor original.

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

Expansión

Frente addy addAllmétodos, hay ensureCapacityInternal1un método para que los algoritmos de expansión
miren la expansión del código relevante:

//①传入列表长度值
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);
}

análisis:

  1. Primero pasamos un valor, que representa la longitud de nuestra lista después de agregar o agregar todo
  2. No puede expandir la matriz cada vez que agrega ; de lo contrario, el costo será demasiado alto, por lo que la expansión de la matriz no solo aumentará la longitud en uno cada vez.
  3. Si actualmente es una matriz vacía, tome el máximo del valor predeterminado y el valor pasado. Para crear una matriz de longitud 10 directamente por primera vez, de lo contrario, agregue un elemento para expandir una vez, lo cual es costoso
  4. Calcule si se necesita expansión
  5. Operación de expansión, en circunstancias normales, la capacidad de la matriz se amplía 1,5 veces , dos casos especiales:
    1. Agregue el elemento por primera vez, expanda directamente a la longitud predeterminada de 10
    2. Hay muchos elementos addAll, y la longitud total es más de 1,5 veces la longitud original, expanda directamente a esta longitud
  6. Copie todos los datos originales en la nueva matriz expandida a través del método de copia de matriz y finalmente asigne la matriz a elementData
  7. Expansión completada

para resumir

  1. ArrayList se implementa en función de una matriz, la longitud predeterminada de la matriz es 10, todas las operaciones son operaciones en la matriz mantenida
  2. Los métodos add y addAll pueden activar la expansión de la matriz
  3. Cuando la longitud no es suficiente, la longitud de expansión es generalmente 1,5 veces la longitud existente.
  4. La expansión, eliminación, etc. se logran copiando la matriz, por lo que la lista no debe ser demasiado larga, de lo contrario, ¡cada copia consumirá demasiado rendimiento!

Supongo que te gusta

Origin blog.csdn.net/lizebin_bin/article/details/88876878
Recomendado
Clasificación