我理解容量是ArrayList中可能包含或不包含引用对象的值的元素或可用空间的数量。我试图更多地了解容量的概念。
所以我有三个问题:
1)从内存的角度来定义容量代表的哪些好方法是什么?
...分配给ArrayList的(连续?)内存?
...... ArrayLists在(堆?)上的内存占用?
2)如果上述情况属实,那么改变容量需要某种方式的内存管理开销?
3)任何人都有一个例子,其中#2是或可能是一个性能问题?可能还有大量的大型ArrayLists不断调整其容量?
答案 0 :(得分:3)
答案 1 :(得分:2)
ArrayList的实现方式如下:
class ArrayList {
private Object[] elements;
}
容量是该数组的大小。
现在,如果您的容量为10,并且您要添加第11个元素,则ArrayList将执行此操作:
Object[] newElements = new Object[capacity * 1.5];
System.arraycopy(this.elements, newElements);
this.elements = newElements;
因此,如果你从一个小容量开始,ArrayList将最终创建一堆数组并在你不断添加元素时为你复制东西,这是不好的。
另一方面,如果您指定容量1,000,000并且只向ArrayList添加3个元素,那也有点不好。
经验法则:如果您知道容量,请指定它。如果您不确定但知道上限,请指定。如果您不确定,请使用默认值。
答案 2 :(得分:1)
容量就像你描述的那样 - 分配给ArrayList以存储值的连续内存。 ArrayList将所有值存储在数组中,并自动为您调整数组大小。这在调整大小时会导致内存管理开销。
如果我没记错的话,当你尝试添加一个元素而不是容量时,Java会将ArrayList的后备阵列的大小从大小N增加到大小为2N + 2。我不知道当您使用insert
方法(或类似方法)插入超出容量结束的特定位置时,或者是否允许这样做时,它会增加到什么大小。
这是一个帮助您思考其工作原理的示例。将|
s之间的每个空格描绘为后备阵列中的单元格:
| | |
size = 0(不包含元素),capacity = 2(可包含2个元素)。
|1| |
size = 1(包含1个元素),capacity = 2(可包含2个元素)。
|1|2|
size = 2,capacity = 2.添加另一个元素:
|1|2|3| | | |
尺寸增加1,容量增加到6(2 * 2 + 2)。对于大型数组而言,这可能是昂贵的,因为分配大的连续内存区域可能需要一些工作(而不是分配许多小块内存的LinkedList),因为JVM需要搜索适当的位置,并且可能需要要求操作系统获得更多内存。将大量值从一个地方复制到另一个地方也很昂贵,一旦找到这样的地区就会这样做。
我的经验法则是:如果您知道所需的容量,请使用ArrayList,因为只有一个分配,访问速度非常快。如果您不知道所需的容量,请使用LinkedList,因为添加新值总是需要相同的工作量,并且不涉及复制。
答案 3 :(得分:0)
1)从内存角度定义容量代表的哪些好方法是什么?
...分配给ArrayList的(连续?)内存?
是的,ArrayList由数组备份,表示内部数组大小。
... ArrayLists在(堆?)上的内存占用?
是的,阵列容量越大,arraylist使用的占用空间越多。
2)如果上述情况属实,那么更改容量需要某种方式的内存管理开销?
是的。当列表变得足够大时,将分配更大的数组并复制内容。之前的数组可能会被丢弃并标记为垃圾回收。
3)任何人都有一个例子,其中#2是或可能是性能问题?可能还有大量的大型ArrayLists不断调整其容量?
是的,如果您创建初始容量为1的ArrayList(例如),并且您的列表会超出该列表。如果您事先知道要存储的元素数量,则最好请求该大小的初始容量。
然而我认为这应该在您的优先级列表中较低,而阵列复制可能经常发生,它从Java的早期阶段开始优化,并且不应该成为一个问题。我认为最好是选择正确的算法。请记住:Premature optimization is the root of all evil