2010-04-20 18 views
5

En mi clase Data Structures, hemos estudiado la clase Java ArrayList y cómo crece la matriz subyacente cuando un usuario agrega más elementos. Eso se entiende. Sin embargo, no puedo entender cómo exactamente esta clase libera memoria cuando se eliminan muchos elementos de la lista. En cuanto a la fuente, hay tres métodos que eliminan elementos:Java: Cómo ArrayList gestiona la memoria

public E remove(int index) { 
RangeCheck(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; // Let gc do its work 

return oldValue; 
} 

public boolean remove(Object o) { 
if (o == null) { 
      for (int index = 0; index < size; index++) 
    if (elementData[index] == null) { 
     fastRemove(index); 
     return true; 
    } 
} else { 
    for (int index = 0; index < size; index++) 
    if (o.equals(elementData[index])) { 
     fastRemove(index); 
     return true; 
    } 
     } 
return false; 
} 


private void fastRemove(int index) { 
     modCount++; 
     int numMoved = size - index - 1; 
     if (numMoved > 0) 
      System.arraycopy(elementData, index+1, elementData, index, 
          numMoved); 
     elementData[--size] = null; // Let gc do its work 
} 

Ninguno de ellos reducir el array almacén de datos. Incluso empecé a preguntarme si la memoria libre nunca ocurre, pero las pruebas empíricas demuestran que sí lo hace. Entonces debe haber alguna otra forma en que se haga, pero ¿dónde y cómo? Revisé las clases para padres también sin éxito.

+0

Por favor reformatee su código. Stackoverflow no entiende [código], edite su publicación, seleccione el fragmento de código y presione el botón "Código" para formatearlo. – Behrang

+0

Sí, esta es mi primera publicación y estaba en el proceso de averiguar cómo usar el formato. Alguien me ayudó :) – cka3o4nik

Respuesta

9

No reducen la matriz subyacente. Simplemente disminuyen el tamaño. El razonamiento para esto es que si tiene 1000 elementos en una matriz y elimina 1, ¿por qué reasignar y copiar la matriz? Es un enorme desperdicio por muy poca ganancia.

Básicamente Java ArrayList s tienen dos propiedades importantes y es importante entender que son diferentes:

  • tamaño: cuántos elementos son teóricamente en el List; y

  • capacidad: cuántos elementos puede ajuste de la matriz subyacente.

Cuando un ArrayList expande, crece alrededor de un 50% en el tamaño, incluso si sólo va a añadir un elemento. Este es un principio similar en reversa. Básicamente se trata de esto: la reasignación de la matriz y la copia de los valores es (relativamente) costosa. Tanto es así que quiere minimizar que ocurra. Siempre que el tamaño teórico sea con una fábrica de aproximadamente 2 del tamaño de la matriz, simplemente no vale la pena preocuparse.

+0

Sí, entiendo que sería una tontería reasignar después de eliminar un elemento. Pero, ¿qué sucede si agrego un millón de elementos y luego los elimino a todos menos uno, y nunca agrego nada hasta que finalice el programa? Sería un desperdicio de memoria mantener un arreglo de un millón de celdas en lugar del tamaño predeterminado. – cka3o4nik

+2

@ cka3o4nik: es solo una serie de referencias. Un millón de ellos todavía ocupa solo 4MB, realmente no vale la pena preocuparse por un caso tan raro. –

+2

@ cka304nik como dice Michael, la reducción del tamaño de la matriz no se considera un gran problema. Si desea hacer esto, puede llamar a 'ArrayList.trimToSize()'. – cletus

1

Tengo que echarle otro vistazo al código fuente de ArrayList, pero remove elimina el objeto de la matriz, y luego si el objeto no está referenciado por ningún otro objeto, el GC puede eliminar ese objeto. Pero el tamaño de la matriz no se reduce.

0

No hay mucha ganancia cambiando el tamaño de la matriz interna ArrayList, incluso si está utilizando ArrayList para contener un objeto grande.

List<LargeObject> list = new ArrayList<LargetObject>(); 

lista solo retendrá la referencia a la instancia de LargeObject y no contendrá la propia instancia de LargeObject.

La referencia no consume mucho espacio. (Piénselo como un puntero en C)

+0

Es extraño cómo las personas tienen tanto miedo de los punteros en C y están tan bien con la idea * de referencia * en Java. – Pijusn

0

El tamaño de la matriz nunca se reduce automáticamente. Es muy raro tener una lista que primero se llena con una gran cantidad de elementos, luego se vacía, pero se mantiene. Y tenga en cuenta que debe haber memoria suficiente para mantener la lista (que consiste únicamente en referencias) y sus elementos; es poco probable que la memoria consumida por la lista vacía sea un problema.

Si realmente encuentra un algoritmo lo suficientemente extraño como para que esto se convierta en un problema, puede liberar la memoria llamando al trimToSize() manualmente.

+0

Buenos puntos, gracias. – cka3o4nik

4

Una ArrayList no retrocede automáticamente, hasta donde yo sé. Sin embargo, puede decir algo como:

ArrayList al = new ArrayList(); 

// fill the list for demo's sake 
for (int i = 0; i < 1000000; ++i) 
{ 
    al.add(i); 
} 

// now remove all but one element 
al.removeRange(1, al.size()); 

// this should shrink the array back to a decent size 
al.trimToSize(); 

Nota: la cantidad de memoria disponible probablemente no cambiará hasta que el GC vuelva a funcionar.

+0

Terminé copiando el código fuente de ArrayList en mi proyecto de prueba y agregué un método getCapacity() para llegar a la matriz subyacente. Como ustedes predijeron, nunca baja a menos que lo solicite manualmente por trimToSize(). Gracias. – cka3o4nik