В моем классе Структур данных мы имеем, изучает класс Java ArrayList, и как это выращивает основной массив, когда пользователь добавляет больше элементов. Это понято. Однако я не могу выяснить, как точно этот класс освобождает память, когда много элементов удалено из списка. При рассмотрении источника существует три метода, которые удаляют элементы:
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
}
Ни один из них не уменьшает массив хранилища данных. Я даже начал подвергать сомнению, происходит ли память, свободная когда-либо, но эмпирические тесты показывают, что она делает. Таким образом, должен быть некоторый другой способ, которым это сделано, но где и как? Я проверил родительские классы также без успеха.
Они не уменьшают базовый массив. Они просто уменьшают размер. Причина в том, что если у вас есть 1000 элементов в массиве и вы удалили 1, зачем перераспределять и копировать массив? Это очень расточительно ради очень небольшой выгоды.
В основном Java ArrayList
имеет два важных свойства, и важно понимать, что они разные:
размер: сколько элементов условно в Список
; и
емкость: сколько элементов может поместиться в базовый массив.
Когда ArrayList
расширяется, он увеличивается примерно на 50%, даже если вы добавляете только один элемент. Это аналогичный принцип в обратном порядке. В основном это сводится к следующему: перераспределение массива и копирование значений (относительно) дорого. Настолько, что вы хотите свести это к минимуму. Пока условный размер фабрики равен примерно 2 размерам массива, об этом просто не стоит беспокоиться.
Мне нужно еще раз взглянуть на исходный код ArrayList, но remove
удаляет объект из массива, а затем, если объект на который не ссылаются никакие другие объекты, сборщик мусора может удалить этот объект. Но размер массива не уменьшается.
Размер массива никогда не уменьшается автоматически. Очень редко действительно есть список, который сначала заполняется большим количеством элементов, а затем он очищается, но все еще сохраняется. И имейте в виду, что должно быть достаточно памяти для хранения списка (который состоит только из ссылок) и его элементов - тогда маловероятно, что память, потребляемая пустым списком, будет проблемой.
Если вы действительно столкнетесь с алгоритмом, достаточно странным, чтобы это стало проблемой, вы все равно можете освободить память, вызвав trimToSize ()
вручную.
Насколько мне известно, список ArrayList не сокращается автоматически. Однако вы можете сказать что-то вроде:
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();
Обратите внимание, объем доступной памяти, вероятно, не изменится, пока GC снова не запустится.
Изменение размера внутреннего массива ArrayList не дает большого преимущества, даже если вы используете ArrayList для хранения большого объекта. Список
List<LargeObject> list = new ArrayList<LargetObject>();
будет содержать только ссылку на экземпляр LargeObject, но не сам экземпляр LargeObject.
Ссылка не занимает много места. (Считайте это указателем в C)