【问题标题】:Why is the hash code returned by the hashCode() method generated by Eclipse not very good?为什么Eclipse生成的hashCode()方法返回的hash码不是很好?
【发布时间】:2014-09-25 13:16:13
【问题描述】:

我通常让Eclipse为我生成hashCode()方法,但现在我发现生成的hash码可能不是很好的迹象。

在哈希集中使用 Eclipse 生成的 hashCode() 方法返回的哈希码比使用手动编码的 hashCode() 方法慢大约 6 倍。

这是我的测试:

  • 具有三个 int 字段的类。
  • hashCode() 和 equals() 方法已由 Eclipse 生成。
  • 用 1.000.000 个类的实例填充 HashSet。
  • 在 HashSet 中查找每个实例
  • 重复查找 10 次。

代码:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class MyPojo {

    private final int a;
    private final int b;
    private final int c;

    public MyPojo(int a, int b, int c) {
        super();
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public static void main(String[] args) {

        List<MyPojo> listOfPojos = new ArrayList<MyPojo>();
        Set<MyPojo> setOfPojos = new HashSet<MyPojo>();

        for (int countA = 0; countA < 100; countA++) {
            for (int countB = 0; countB < 100; countB++) {
                for (int countC = 0; countC < 100; countC++) {
                    MyPojo myPojo = new MyPojo(countA, countB, countC);
                    listOfPojos.add(myPojo);
                    setOfPojos.add(myPojo);
                }
            }
        }

        long startTime = System.currentTimeMillis();

        for (int count = 0; count < 10; count++) {
            for (MyPojo myPojo : listOfPojos) {
                if (!setOfPojos.contains(myPojo)) {
                    throw new RuntimeException();
                }
            }
        }

        long endTime = System.currentTimeMillis();
        System.out.format("Execution time: %3f s", (endTime - startTime) / 1000.0);

    }

    // Generated by Eclipse
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + a;
        result = prime * result + b;
        result = prime * result + c;
        return result;
    }

    // Generated by Eclipse
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MyPojo other = (MyPojo) obj;
        if (a != other.a)
            return false;
        if (b != other.b)
            return false;
        if (c != other.c)
            return false;
        return true;
    }

}

在我的机器上,这会导致大约 1.23 秒的执行时间。

现在用我在别处找到的这个方法替换 hashCode() 方法:

@Override
public int hashCode() {
    final int magic = 0x9e3779b9;
    int seed = 0;
    seed ^= this.a + magic + (seed << 6) + (seed >> 2);
    seed ^= this.b + magic + (seed << 6) + (seed >> 2);
    seed ^= this.c + magic + (seed << 6) + (seed >> 2);
    return seed;
}

现在执行时间只有 0.2 秒,快了大约 6 倍!

为什么?


编辑:

按照建议,计算哈希值“再次出现”的次数得出以下结果:

使用 Eclipse 生成的 hashCode() 方法:

1: 62
2: 62
3: 1406
4: 440
5: 62
6: 1406
7: 62
8: 440
9: 52094
10: 4670
11: 4670
12: 26144
13: 1358
14: 1358
15: 1358
16: 2716

使用手工编码的 hashCode() 方法:

1: 79093
2: 180316
3: 23444
4: 107020
5: 2213
6: 6821
7: 296
8: 960
10: 12

所以 Eclipse 生成的方法只给出了只出现一次的 62 个哈希码。

手工编码的版本给出了79093个只出现一次的哈希码,180316个只出现了两次的哈希码。

传播差异很大。


编辑 2:

还尝试了 Objects.hash(...),与 Eclipse 生成的 hashCode() 方法相比,这给出了相同的“重新出现”计数。

@Override
public int hashCode() {
    return Objects.hash(a, b, c);
}

此外,这实际上更减慢了执行速度:1.38 秒


编辑 3:

这里解释了上面更好的哈希码方法中的“幻数”来自哪里:

Magic number in boost::hash_combine


编辑 4:使用http://projectlombok.org 生成 hashCode() 和 equals() 方法

Lombok 给出了迄今为止最好的结果:

1: 33958
2: 146124
3: 8118
4: 162360

Execution time: 0.187000 s

【问题讨论】:

  • 我不明白您所做的如何可以称为正确的“基准测试”。如果你想测试 hashCode 你应该调用这个方法
  • 问题不在于 hashCode() 方法的执行时间。请阅读代码。
  • "为什么Eclipse生成哈希码的方法效率那么低"
  • 抱歉,更改了误导性标题。
  • 这里有一些你可能会感兴趣的东西:保持一个计数器,记录两种方法中每个哈希码值出现的次数。我怀疑你会在第一个发现很多重复。

标签: java eclipse hash


【解决方案1】:

Eclipse hashCode() 遵循Effective Java 中建议的指南。作者说方法还不错,但绝对不是最好的。

如果hashCode 的性能不令人满意,您可以随意寻找替代方案。

我还想提一提的是,对于几乎每个hashCode 函数,您可能能够找到一些数据集来阻止函数均匀分布散列值,从而使HashSet 在你的代码就像一个长长的列表。

您可以在此处查看其他讨论:Is the hashCode function generated by Eclipse any good?

您可能还想阅读this

【讨论】:

    猜你喜欢
    • 2011-05-30
    • 2012-08-01
    • 1970-01-01
    • 2010-11-20
    • 2015-03-02
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 2015-03-07
    相关资源
    最近更新 更多