【问题标题】:What happens when HashMap or HashSet maximum capacity is reached?当 HashMap 或 HashSet 达到最大容量时会发生什么?
【发布时间】:2012-01-24 17:08:57
【问题描述】:

就在几分钟前,我回答了一个关于“Java 中 HashMap 的最大可能大小”的问题。正如我一直读到的,HashMap 是一个可增长的数据结构。它的大小仅受JVM内存大小的限制。因此我认为它的大小没有硬性限制并相应地回答。 (同样适用于 HashSet。)

但有人纠正我说,由于 HashMap 的 size() 方法返回一个 int,因此 限制了它的大小。一个完全正确的观点。我只是尝试在本地测试它但失败了,我需要超过 8GB 的​​内存才能在 HashMap 中插入超过 2,147,483,647 个整数,而我没有。

我的问题是:

  • 当我们尝试将 2,147,483,647 + 1 个元素插入到 HashMap/HashSet?
  • 是否抛出错误?
  • 如果是,是哪个错误?如果 HashMap/HashSet 没有发生什么,它已经 现有元素和新元素?

如果有人有幸使用 16GB 内存的机器,您可以实际尝试一下。 :)

【问题讨论】:

  • 属于 MapOverflow.com
  • 您不需要 16 GB RAM。只需获取 64 位版本的 Windows 并创建一个页面文件供其余部分进行测试。
  • 你查看过 HashMap 的源代码吗?然后,您将很快了解为什么它会被整数大小限制。
  • @ThomasJungblut:感谢您的评论,我会看看。但我试图弄清楚如果我们在其中插入超过整数限制数量的项目会发生什么。
  • @Kerrek:我不明白 - 只是另一个 SE 衍生产品?

标签: java collections size hashmap overflow


【解决方案1】:

数组的底层容量必须是 2 的幂(限制为 2^30)当达到这个大小时,负载因子被有效地忽略并且数组停止增长。

此时碰撞率增加。

鉴于 hashCode() 只有 32 位,因此在任何情况下都变得如此之大是没有意义的。

/**
 * Rehashes the contents of this map into a new array with a
 * larger capacity.  This method is called automatically when the
 * number of keys in this map reaches its threshold.
 *
 * If current capacity is MAXIMUM_CAPACITY, this method does not
 * resize the map, but sets threshold to Integer.MAX_VALUE.
 * This has the effect of preventing future calls.
 *
 * @param newCapacity the new capacity, MUST be a power of two;
 *        must be greater than current capacity unless current
 *        capacity is MAXIMUM_CAPACITY (in which case value
 *        is irrelevant).
 */
void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }

    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable);
    table = newTable;
    threshold = (int)(newCapacity * loadFactor);
}

当大小超过 Integer.MAX_VALUE 时,它会溢出。

void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
    if (size++ >= threshold)
        resize(2 * table.length);
}

【讨论】:

  • 你能解释一下为什么限制在 2^30,我的意思是 30 是从哪里来的?为什么不能变成 31、32……?
  • 数组的大小限制为带符号的 32 位数字。这是一个历史限制,不幸的是,它很难修复以允许签署长尺寸。最大有符号 int 值为 2^31-1。然而,数组的大小必须是 2 的幂(由于 HashMap 的工作方式),而且这个数太少了,所以它可以是 2 的最大幂 2 ^30。鉴于 hashCode 只有 2^32 个可能的值,在任何情况下拥有更多的值是毫无意义的。 ;)
  • 由于这些原因,我使用更大的集合,包括哈希映射,并使用 64 位大小/长度和 64 位哈希码。
  • 所以你的意思是你可以在 hashmap 中存储超过 2,147,483,647 个项目?如果是,你是怎么做到的?我想知道。
  • 你可以在HashMap中存储值,但是大小会出错。但是,我使用的是使用散列的映射,或一般意义上的散列映射。这种哈希映射的问题是它的剪切大小。假设您想为键、值和任何开销存储 256 个字节。单个集合的容量为 512 GB,超过了您的内存大小,并且可能超过了一个驱动器的可用空间。 (使用 SSD 可以比使用 HDD 进行随机访问快 1000 倍)
猜你喜欢
  • 2012-10-22
  • 2015-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
相关资源
最近更新 更多