【问题标题】:Maximum size of HashSet, Vector, LinkedListHashSet、Vector、LinkedList 的最大大小
【发布时间】:2011-10-03 07:25:47
【问题描述】:

HashSetVectorLinkedList 的最大大小是多少?我知道ArrayList可以存储超过3277000个号码。

但是列表的大小取决于内存(堆)大小。如果达到最大值,JDK 会抛出 OutOfMemoryError

但我不知道HashSetVectorLinkedList中元素的数量限制。

【问题讨论】:

    标签: java collections


    【解决方案1】:

    这些结构没有指定的最大大小。

    实际的实际大小限制可能在Integer.MAX_VALUE 范围内(即 2147483647,大约 20 亿个元素),因为这是 Java 中数组的最大大小。

    • HashSet 在内部使用HashMap,因此它的最大大小与
      • HashMap 使用的数组的大小始终是 2 的幂,因此它最多可以是 230 = 1073741824 个元素大(因为下一个 2 的幂更大比Integer.MAX_VALUE)。
      • 通常元素的数量最多是桶的数量乘以负载因子(默认为 0.75)。 然而,当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

    请注意,虽然 HashMapHashSetLinkedList 支持的元素不止Integer.MAX_VALUE,但没有一个实现size() 方法以这种方式(即他们只是让内部size 字段溢出)。

    这让我相信其他操作在这种情况下没有明确定义。

    所以我会说使用这些具有高达 Integer.MAX_VLAUE 元素的通用集合是安全。如果你知道你需要存储更多,那么你应该切换到真正支持这个的专用集合实现。

    【讨论】:

    • HashMap 使用数组进行 first 查找。但是如果发生键冲突,这些将存储在一个链表中。因此,HashMap 可能包含多个 Integer.MAX_VALUE 元素 - 以一种不可预测的方式。
    • 对于 LinkedList(实际上这适用于所有列表),get(int) 函数也接受整数,这意味着您不能使用它来检索元素。在任何情况下,我都不会押注 LinkedList 在 Integer.MAX_VALUE 之上的行为符合预期。
    • HashMap 的限制是负载因子 * 十亿。在此之后,它将无法增长底层数组。向量不会增长到 Integer.MAX_VALUE,您必须创建具有此大小的向量作为初始容量。 (不太可能)size() 文档返回大于此大小的 Integer.MAX_VALUE,因此恕我直言,LinkedList 的 size() 没有错误。
    • 我认为您不适合 HashMap / HashSet。确实,哈希数组仅限于2^30。但是,您可以继续向表中添加元素ad infinitum,因为哈希链是简单的链表。 (性能会随着哈希链的增长而下降,但这是一个不同的问题。)见docjar.com/html/api/java/util/HashMap.java.html line 764
    • @StephenC, @AH:你是对的,一旦达到限制,它就会停止调整大小,所以 HashMap/HashSet 的行为方式与之后的 LinkedList 相同(增长无限制)。我会更新我的答案。
    【解决方案2】:

    在所有情况下,您都可能受到 JVM 堆大小而不是其他任何因素的限制。最终你会一直使用数组,所以我非常怀疑它们中的任何一个会管理超过 231 - 1 个元素,但在那之前你很可能会用完堆无论如何。

    【讨论】:

      【解决方案3】:

      这在很大程度上取决于实现细节。

      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
      【解决方案4】:

      最大大小取决于 JVM 的内存设置,当然还有可用的系统内存。每个列表条目的具体内存消耗大小也因平台而异,因此最简单的方法可能是运行简单的测试。

      【讨论】:

        【解决方案5】:

        如其他答案所述,数组不能达到 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)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-14
          • 1970-01-01
          • 2019-04-20
          • 2020-06-24
          • 1970-01-01
          • 2012-07-04
          • 1970-01-01
          • 2010-09-20
          相关资源
          最近更新 更多