0

我知道容量是 ArrayList 中可能包含也可能不包含引用对象的值的元素或可用空间的数量。我试图更多地了解容量的概念。

所以我有三个问题:

1)从内存的角度定义容量代表什么的好方法是什么?

...分配给 ArrayList 的(连续?)内存?

... ArrayLists 在(堆?)上的内存占用?

2)如果上述情况属实,那么改变容量需要某种方式的内存管理开销?

3)任何人都有一个例子,其中#2是或可能是性能问题?除了可能不断调整容量的大量大型 ArrayList 之外?

4

4 回答 4

3
  1. 该类之所以称为 ArrayList,是因为它基于一个数组。容量是数组的大小,需要一块连续的堆内存。但是,请注意,数组本身仅包含对元素的引用,这些元素是堆上的单独对象。
  2. 增加容量需要分配一个新的、更大的数组并将所有引用从旧数组复制到新数组,之后旧数组就有资格进行垃圾回收。
  3. 您已经引用了性能可能成为问题的主要案例。在实践中,我从未见过它实际上成为问题,因为元素对象通常比列表占用更多的内存(可能还有 CPU 时间)。
于 2011-03-23T20:31:02.770 回答
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 个元素,那也有点糟糕。

经验法则:如果您知道容量,请指定它。如果您不确定但知道上限,请指定。如果您不确定,请使用默认值。

于 2011-03-23T20:30:33.520 回答
1

容量正如您所描述的那样 - 分配给 ArrayList 用于存储值的连续内存。ArrayList 将所有值存储在一个数组中,并自动为您调整数组的大小。这会在调整大小时产生内存管理开销。

如果我没记错的话,当您尝试添加一个超出容量的元素时,Java 会将 ArrayList 的后备数组的大小从 N 增加到 2N + 2。我不知道当您使用该insert方法(或类似方法)在超出容量末端的特定位置插入时它会增加多少大小,甚至不知道它是否允许这样做。

这里有一个例子可以帮助你思考它是如何工作的。将 s之间的每个空间想象|成后备数组中的一个单元:

| | |

size = 0(不包含元素),容量 = 2(可以包含 2 个元素)。

|1| |

size = 1(包含 1 个元素),容量 = 2(可以包含 2 个元素)。

|1|2|

大小 = 2,容量 = 2。添加另一个元素:

|1|2|3| | | |

大小增加 1,容量增加至 6 (2 * 2 + 2)。对于大型数组,这可能会很昂贵,因为分配一个大的连续内存区域可能需要一些工作(与分配许多小块内存的 LinkedList 不同),因为 JVM 需要搜索适当的位置,并且可能需要要求操作系统提供更多内存。将大量值从一个地方复制到另一个地方也很昂贵,一旦找到这样的区域就会这样做。

我的经验法则是:如果您知道需要的容量,请使用 ArrayList,因为只有一次分配并且访问速度非常快。如果您不知道所需的容量,请使用 LinkedList,因为添加新值始终需要相同的工作量,并且不涉及复制。

于 2011-03-23T20:33:28.577 回答
0

1)从内存的角度定义容量代表什么的好方法是什么?

...分配给 ArrayList 的(连续?)内存?

是的,一个 ArrayList 由一个数组备份,它代表内部数组的大小。

... ArrayLists 在(堆?)上的内存占用?

是的,阵列容量越大,arraylist 使用的内存就越多。

2)如果上述情况属实,那么改变容量需要某种方式的内存管理开销?

这是。当列表变得足够大时,分配一个更大的数组并复制内容。先前的数组可能会被丢弃并标记为垃圾收集。

3)任何人都有一个例子,其中#2是或可能是性能问题?除了可能不断调整容量的大量大型 ArrayList 之外?

是的,如果您创建初始容量为 1 的 ArrayList (例如)并且您的列表远远超出此范围。如果您预先知道要存储的元素数量,则最好请求该大小的初始容量。

但是我认为这在您的优先级列表中应该很低,虽然数组复制可能经常发生,但它在 Java 的早期阶段就已经优化,不应该成为问题。我认为最好选择一个正确的算法。记住:过早的优化是万恶之源

另请参阅:何时在 ArrayList 上使用 LinkedList

于 2011-03-23T20:45:34.407 回答