Hacer preguntas
- ¿Basado en que? ¿Formación? ¿Lista enlazada? ¿cola?
- ¿Por qué puedes quedarte con el
add
elemento?
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;
ArrayList
Todo opera internamente add、remove、set、get
en elementData
esta matriz, por lo que la ArrayList
implementació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
ArrayList
Se definen tres métodos de construcción. No son de construcción de parámetros. Se int
pasa 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.
- 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);
}
}
- Sin construcción de parámetros, apuntará
elementData
a la matriz predeterminada, longitud 0 - Pase una lista.
Arrays.copyOf()
Copie la lista pasada a la nueva matriz por método y luegoelementData
apunte 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+1
y 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 elementData
ella, 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 add
y addAll
métodos, hay ensureCapacityInternal1
un 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:
- Primero pasamos un valor, que representa la longitud de nuestra lista después de agregar o agregar todo
- 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.
- 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
- Calcule si se necesita expansión
- Operación de expansión, en circunstancias normales, la capacidad de la matriz se amplía 1,5 veces , dos casos especiales:
- Agregue el elemento por primera vez, expanda directamente a la longitud predeterminada de 10
- Hay muchos elementos addAll, y la longitud total es más de 1,5 veces la longitud original, expanda directamente a esta longitud
- 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
- Expansión completada
para resumir
- 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
- Los métodos add y addAll pueden activar la expansión de la matriz
- Cuando la longitud no es suficiente, la longitud de expansión es generalmente 1,5 veces la longitud existente.
- 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!