【问题标题】:Internal HashMap working : How to implement hashCode in java内部 HashMap 工作:如何在 java 中实现 hashCode
【发布时间】:2016-12-12 22:26:12
【问题描述】:

我正在努力为下面给出的 Student 类编写正确的 hashCode 函数。

1) 我认为 hashCode 应该足够好,以至于两个不同对象的 hashCode 不应该相互碰撞。

观察:对于这个实现,当我调试并检查“HashMap 的内部表对象”类时,我发现 HashMap 中的每个条目都分配了不同的存储桶位置。

问题:在每个索引处都有一个桶(列表/树)的目的是什么。

实施:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + id;
    return result;
}

2) 如果我允许 hashCode 冲突:

观察:对于这个实现,当我调试和检查时,发现“hashMap 内部表的大小”不断增加,并且只有在 hashCode 范围内的桶被使用。其余存储桶索引显示为空。

问题:如果超出 hashCode 范围的存储桶始终为空,增加内部表大小的目的是什么。

实施:

@Override
public int hashCode() {
    return id%20;
}

需要帮助以正确实现 hashCode,以便可以修复上述问题。 提前感谢您的帮助。

============================代码================== =========

public class HashMapTest {

public static void main(String a[]) {
    HashMap<Student, Integer> set = new HashMap<Student, Integer>();

    for (int i = 0; i < 5000; i++) {
        set.put(new Student(i), i);
    }

    set.put(new Student(5001), 5001);
    System.out.println(set.size());
}
}

class Student {
private int id;

public Student(int id) {
    this.id = id;
}

// Add here hashCode() provided in comments.


@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Student other = (Student) obj;
    if (id != other.id)
        return false;
    return true;
}

}

【问题讨论】:

  • 假设学生由id唯一标识,只需直接使用id作为您的哈希码。没必要弄坏它。如果您想了解更多关于 HashMap 内部的信息,网上有很多文章描述了哈希映射的工作原理,例如请参阅有关 Hash table (相同事物,不同名称) 的维基百科文章。
  • > "如果我允许 hashCode 冲突"。通常,无论散列函数如何,散列码冲突都是不可避免的。 Java 中的哈希码是一个整数,因此允许大约 2^32 个不同的值。生成 2^32 + 1 个不同的 Java 对象(例如 Longs)是微不足道的,因此可以保证至少发生一次冲突。话虽如此,您确实希望尽可能减少碰撞的可能性,@Andreas 的建议非常有意义。

标签: java hashmap hashcode


【解决方案1】:

在每个索引处都有一个桶(列表/树)的目的是什么。

HashMap 并不要求 hashCode 是唯一的,因为这一般是做不到的(比如有 2^32 个 hashcode,但是Strings 是无限多的,所以不可能对每个 @ 使用不同的 hashCode 987654323@)。相反,它只要求冲突很少

因此,HashMap 的实现使得即使发生冲突它仍然可以正常工作(尽管在这种情况下它可能会运行得更慢)。这就是为什么 HashMap 使用可以在必要时存储多个元素的存储桶。

如果超出 hashCode 范围的存储桶始终为空,增加内部表大小的目的是什么。

HashMap 会调整表的大小,因为这样做会拆分存储桶。通常,拆分一个桶会导致一些元素进入一个桶,一些元素进入另一个桶,从而提高性能。它没有意识到您的 hashCode 如此糟糕以至于所有元素都将保留在同一个存储桶中,因此请继续尝试:-)

需要帮助以正确实现 hashCode,以便可以修复上述问题。

我会用

@Override
public int hashCode() {
    return id;
}

如果 id 是唯一的(它的名字似乎暗示了这一点),这是一个完美的哈希函数,它甚至可以快速计算 :-)

(请注意,hashCode 可能大于表大小;HashMap 将通过在必要时截断它来解决这个问题)

【讨论】:

  • 还应该提到.hashCode().equals(Object) 应该始终成对实现。因为在识别出正确的存储桶后,.equals(Object) 方法用于查找匹配的对象。 -- 另请参阅此答案:stackoverflow.com/a/27609/4791599
  • 既然 OP 正确实现了equals,我觉得没有必要提这个,但你肯定是正确的,equal 对象必须等于 hashCodes
  • 感谢您的回答......完美解决了所有疑问。
猜你喜欢
  • 2017-03-30
  • 2020-06-21
  • 2014-09-29
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-19
  • 2019-05-20
相关资源
最近更新 更多