【发布时间】:2011-10-03 07:25:47
【问题描述】:
HashSet、Vector、LinkedList 的最大大小是多少?我知道ArrayList可以存储超过3277000个号码。
但是列表的大小取决于内存(堆)大小。如果达到最大值,JDK 会抛出 OutOfMemoryError。
但我不知道HashSet、Vector和LinkedList中元素的数量限制。
【问题讨论】:
标签: java collections
HashSet、Vector、LinkedList 的最大大小是多少?我知道ArrayList可以存储超过3277000个号码。
但是列表的大小取决于内存(堆)大小。如果达到最大值,JDK 会抛出 OutOfMemoryError。
但我不知道HashSet、Vector和LinkedList中元素的数量限制。
【问题讨论】:
标签: java collections
这些结构没有指定的最大大小。
实际的实际大小限制可能在Integer.MAX_VALUE 范围内(即 2147483647,大约 20 亿个元素),因为这是 Java 中数组的最大大小。
HashSet 在内部使用HashMap,因此它的最大大小与
HashMap 使用的数组的大小始终是 2 的幂,因此它最多可以是 230 = 1073741824 个元素大(因为下一个 2 的幂更大比Integer.MAX_VALUE)。HashMap 停止调整大小时,它将仍然允许您添加元素,利用每个桶通过链表管理的事实。因此,HashMap/HashSet 中元素的唯一限制是内存。Vector 在内部使用了一个数组,该数组的最大大小正好是 Integer.MAX_VALUE,因此它不能支持超过这么多的元素LinkedList 不使用数组作为底层存储,因此不会限制大小。它使用没有固有限制的经典双向链表结构,因此其大小仅受可用内存的限制。请注意,如果LinkedList 大于Integer.MAX_VALUE,则会错误报告大小,因为它使用int 字段来存储大小,并且size() 的返回类型也是int。请注意,虽然Collection API确实定义了具有多个 Integer.MAX_VALUE 元素的 Collection 的行为方式。最重要的是它声明了这个the size() documentation:
如果此集合包含多个
Integer.MAX_VALUE元素,则返回Integer.MAX_VALUE。
请注意,虽然 HashMap、HashSet 和 LinkedList 支持的元素不止Integer.MAX_VALUE,但没有一个实现size() 方法以这种方式(即他们只是让内部size 字段溢出)。
这让我相信其他操作也在这种情况下没有明确定义。
所以我会说使用这些具有高达 Integer.MAX_VLAUE 元素的通用集合是安全。如果你知道你需要存储更多,那么你应该切换到真正支持这个的专用集合实现。
【讨论】:
HashMap 使用数组进行 first 查找。但是如果发生键冲突,这些将存储在一个链表中。因此,HashMap 可能包含多个 Integer.MAX_VALUE 元素 - 以一种不可预测的方式。
get(int) 函数也接受整数,这意味着您不能使用它来检索元素。在任何情况下,我都不会押注 LinkedList 在 Integer.MAX_VALUE 之上的行为符合预期。
size() 文档返回大于此大小的 Integer.MAX_VALUE,因此恕我直言,LinkedList 的 size() 没有错误。
2^30。但是,您可以继续向表中添加元素ad infinitum,因为哈希链是简单的链表。 (性能会随着哈希链的增长而下降,但这是一个不同的问题。)见docjar.com/html/api/java/util/HashMap.java.html line 764
HashMap/HashSet 的行为方式与之后的 LinkedList 相同(增长无限制)。我会更新我的答案。
在所有情况下,您都可能受到 JVM 堆大小而不是其他任何因素的限制。最终你会一直使用数组,所以我非常怀疑它们中的任何一个会管理超过 231 - 1 个元素,但在那之前你很可能会用完堆无论如何。
【讨论】:
这在很大程度上取决于实现细节。
HashSet 使用数组作为底层存储,默认情况下它会在集合已满 75% 时尝试增长。这意味着如果您尝试添加超过 750,000,000 个条目,它将失败。 (它不能将数组从 2^30 增加到 2^31 个条目)
增加负载因子会增加集合的最大大小。例如负载因子为 10 允许 100 亿个元素。 (值得注意的是,HashSet 在超过 1 亿个元素时效率相对较低,因为 32 位哈希码的分布开始看起来不那么随机,并且冲突次数增加)
Vector 的容量翻倍,从 10 开始。这意味着它无法增长到超过 13.4 亿。将初始大小更改为 2^n-1 可为您提供更多的头部空间。
顺便说一句:如果可以,请使用 ArrayList 而不是 Vector。
LinkedList 没有固有的限制,可以增长到超过 21 亿。此时 size() 可能会返回 Integer.MAX_VALUE,但是某些函数(例如 toArray)会失败,因为它无法将所有对象放入数组中,而是会为您提供第一个 Integer.MAX_VALUE 而不是抛出异常。
正如@Joachim Sauer 所指出的,当前的 OpenJDK 可能会为大于 Integer.MAX_VALUE 的大小返回不正确的结果。例如它可能是一个负数。
【讨论】:
LinkedList 的 OpenJDK 实现中(我假设在 Oracle JDK 中也是如此),一旦大小超过该值,就无法正确返回 Integer.MAX_VALUE。
最大大小取决于 JVM 的内存设置,当然还有可用的系统内存。每个列表条目的具体内存消耗大小也因平台而异,因此最简单的方法可能是运行简单的测试。
【讨论】:
如其他答案所述,数组不能达到 2^31 个条目。其他数据类型要么受此限制,要么最终可能会误报其 size()。但是,在某些系统上无法达到这些理论极限:
在 32 位系统上,可用的字节数永远不会超过 2^32。这是假设您没有操作系统占用内存。一个 32 位的指针是 4 个字节。任何不依赖数组的东西都必须在每个条目中至少包含一个指针:这意味着对于不使用数组的东西,最大条目数是 2^32/4 或 2^30。
普通数组可以达到理论上的极限,但只有字节数组,长度为 2^31-1 的短数组会占用大约 2^32+38 个字节。
一些 java VM 引入了一种使用压缩指针的新内存模型。通过调整指针对齐,32 字节指针可以引用略多于 2^32 字节。大约是四倍。这足以导致 LinkedList size() 变为负数,但不足以使其回绕为零。
一个 64 位系统有 64 位指针,使所有指针大一倍,使非数组列表更胖。这也意味着支持的最大容量正好跳到 2^64 字节。这足以让 2D 阵列达到其理论最大值。 byte[0x7fffffff][0x7fffffff] 使用的内存大约等于 40+40*(2^31-1)+(2^31-1)(2^31-1)=40+40( 2^31-1)+(2^62-2^32+1)
【讨论】: