在我的Data Structures类中,我们研究了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
}
它们都不会减少数据存储阵列。我甚至开始质疑是否会发生内存释放,但实证测试表明它确实存在。所以必须有其他方式,但在哪里以及如何?我检查了父类,但没有成功。
答案 0 :(得分:10)
它们不会减少底层数组。它们只是减小了尺寸。这样做的原因是,如果数组中有1000个元素并删除1,为什么要重新分配和复制数组?很少收获,这是非常浪费的。
基本上Java ArrayList
有两个重要的属性,重要的是要理解它们是不同的:
尺寸: List
中名义的元素数量;以及
容量 可以在基础数组中适合的元素数量。
当ArrayList
扩展时,即使您只添加一个元素,它的大小也会增加约50%。这是一个相反的原理。基本上它归结为:重新分配数组并复制值(相对)昂贵。这么多,以至于你想尽量减少它的发生。只要名义尺寸的阵列大小约为2的工厂,就不值得担心。
答案 1 :(得分:4)
据我所知,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再次运行。
答案 2 :(得分:1)
我必须再看看ArrayList的源代码,但是remove
从数组中删除了对象,然后如果该对象没有被任何其他对象引用,则GC可以删除该对象。但阵列大小并没有减少。
答案 3 :(得分:0)
通过调整ArrayList内部数组的大小没有太大的收获,即使您使用ArrayList来保存大对象。
List<LargeObject> list = new ArrayList<LargetObject>();
list只保存对LargeObject实例的引用,而不保存LargeObject实例本身。
参考不会占用太多空间。 (把它想象成C中的指针)
答案 4 :(得分:0)
数组大小永远不会自动减少。实际上有一个列表首先填充了大量的元素,然后将其清空但仍然保持不变,这是非常罕见的。并且请记住,必须有足够的内存来保存列表(仅包含引用)及其元素 - 然后空列表消耗的内存不太可能是一个问题。
如果你真的遇到一个奇怪的算法,这会成为一个问题,你仍然可以通过手动调用trimToSize()
来释放内存。