【问题标题】:Why am I getting an OutOfMemoryError resizing my HashTable implementation?为什么我在调整 HashTable 实现的大小时收到 OutOfMemoryError?
【发布时间】:2015-04-11 19:45:10
【问题描述】:

每次发生冲突时,我都会尝试 rehash() 我的 HashTable,但我不断收到 Java 堆空间错误。

基本上,我有一个 String[] 表,每次我的哈希值发生冲突时,我都想将其长度乘以 2。

编辑:我在 while 循环中使用 insert(),它将大约 300.000 个单词加载到哈希表中。

 public void rehash() {
        String[] backup = table;
        size = size * 2;
        // i get the error on the line below
        table = new String[size];
        System.out.println("size" + size);
        for (int i = 0; i < backup.length; i++) {
            if (backup[i] != null) {
                insert(backup[i]);
            }

        }

   public void insert(String str) {

        int index = hashFunction(str);

        if (index > size || table[index] != null) {
            rehash();
        }

        table[index] = str;
    }

我的哈希函数:

int val= 0;
        val= s.hashCode();
        if (val< 0) {
            val*= -1;
        }

        while (val> this.size) {
            val%= this.size;
        }

        return val;


 public void load() {
        String str = null;
        try {
            BufferedReader in = new BufferedReader(new FileReader(location));
            while ((str = in.readLine()) != null) {
                insert(str);
            }
            in.close();
        } catch (Exception e) {
            System.out.println("exception");
        }
    }

【问题讨论】:

标签: java hash out-of-memory hashtable


【解决方案1】:

从您发布的哈希函数不清楚它返回什么,但看起来它有问题。

int index = hashFunction(str);

如果您的索引不正确,那么您的代码正在执行大量递归 new String[size]。在此处放置一个计数器或调试点并检查。

 if (index > size || table[index] != null) {
                rehash();
            }

【讨论】:

  • 我在 rehash() 之前创建了一个计数器;结果是 7。您还需要其他信息吗?我没有得到确切缺少的东西。我已经在使用 string.hashCode(); 的帖子中发布了我的哈希函数;之后进行一些编辑。
  • 您的哈希函数返回一个未在此处发布的值。此外,您的表的初始大小是多少?如果你插入的字符串数量不应该被重新散列 7 次,那么你的散列函数就有错误。否则使用 -Xmx 提供更多内存
  • 哈希函数为每个单词返回超过 300.000 个单词的结果。我的数组的初始大小是 514751。但我不希望发生任何冲突。我应该尝试不同的哈希函数来解决这个问题吗?
  • 你的数组的初始大小是 514751 !??并且您输入了 300 个单词并且内存不足。您的哈希函数有问题。发布其中显示变量 s 和哈希的代码。
  • 在我的问题中添加了 load() 方法。
【解决方案2】:

无论你把桌子做成多大,你都不能完全避免碰撞。试试这个程序,例如:

System.out.println("Aaa".hashCode());
System.out.println("AbB".hashCode());
System.out.println("BBa".hashCode());
System.out.println("BCB".hashCode());

输出是:

65569
65569
65569
65569

它们是四个不同的字符串,具有完全相同的哈希码。这种精确的碰撞甚至不是那么罕见。 (Java String 类使用的哈希算法实际上并不是一个很好的算法,但出于向后兼容的原因保留了它。)

因此,使哈希表更大(使用更大部分的哈希码)可以减少冲突的数量,但永远不会完全阻止它们,因为有时不同值的哈希码完全相同

哈希表必须准备好处理有限数量的冲突,方法是能够在表的单个槽中存储一组不同的值。这通常是通过对共享相同哈希码的值使用链表来完成的。 java.util.HashMap 的当前实现做了一些更高级的事情:如果具有相同哈希码的值实现了Comparable 接口(就像String 所做的那样),它使用它将它们排列在二叉树中。还有一种可能称为动态完美散列,通过动态更改散列算法以确保每个不同的值获得不同的散列来防止冲突,但这更复杂。

我在您的代码中看到的其他一些问题:

  • 如果您立即在下一行为其分配其他内容,则无需将 val 初始化为 0。你可以改用int val; val = s.hashCode(); 或干脆int val = s.hashCode();

  • 检查:if (val &lt; 0) val *= -1; 并不完全可靠,因为如果 val 完全等于 Integer.MIN_VALUE,则将其乘以 -1 会溢出并产生 Integer.MIN_VALUE 作为结果。要完全防止负值,请通过 val &amp;= Integer.MAX_VALUE; 屏蔽整数的符号位。

  • 这里的条件是错误的:while (val &gt; this.size) val %= this.size;。应该是val &gt;= this.size。但是,根本不需要循环。无条件地进行模运算一次 没有while/if 就足够了。或者,如果您将表大小保持为 2 的精确幂,您可以将 mod 操作实现为:val &amp;= (size - 1);,这比% 快一点,并且还可以满足确保结果非负的要求.

  • 在插入方法中它必须是if (index &gt;= size ...,而不是if (index &gt; size ...,但实际上根本不需要检查,如果散列函数已经确保散列在范围内。

  • 当表槽已被占用时,您需要检查它是否已经包含您尝试插入的相同字符串(在这种情况下,您可以立即从该方法返回),而不仅仅是假设它是一个不同的值发生碰撞。

【讨论】:

    【解决方案3】:

    来自javadoc

    作为一般规则,默认负载因子 (.75) 提供了一个很好的 时间和空间成本之间的权衡。较高的值会降低 空间开销,但增加了查找成本(反映在大多数 HashMap 类的操作,包括 get 和 put)。 预期 应考虑地图中的条目数及其负载因子 帐户时设置其初始容量,以尽量减少 rehash 操作的次数。 如果初始容量大于 最大条目数除以负载因子,无rehash 操作将永远发生。

    如果您知道该地图将用于存储大约 N 条记录,则良好的 initialCapacity 将是 N/.75 + N/10 - 考虑到 10% 的方差。

    • 可以得到 OutOfMemory 错误,但不能编程 rehash - 尽量避免它。
    • 对于重新散列 - 你不应该等到碰撞。来自 HashMap 类,

    这个(resize)方法会在这个map中key的数量达到它的阈值时自动调用

    在哪里threshold = (int)(capacity * loadFactor);

    【讨论】:

    • 如何在不重新散列的情况下添加所有数据?
    • 通过选择足够高的初始容量?
    • 即使是最大整数值,我也会遇到冲突。所以一定有问题。
    • 恕我直言,我建议你浏览一下 hashmap 的 javadoc,然后返回你的代码看看你缺少什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    • 2012-05-01
    相关资源
    最近更新 更多