使用Objects.hash()或自己的hashCode()实现?

时间:2017-08-23 06:46:08

标签: java hash

我最近发现了Objects.hash()方法。

我的第一个想法是,这会整理你的hashCode()实施。请参阅以下示例:

@Override
//traditional
public int hashCode() {
    int hash = 5;
    hash = 67 * hash + (int)(this.id ^ (this.id >>> 32));
    hash = 67 * hash + (int)(this.timestamp ^ (this.timestamp >>> 32));
    hash = 67 * hash + Objects.hashCode(this.severity);
    hash = 67 * hash + Objects.hashCode(this.thread);
    hash = 67 * hash + Objects.hashCode(this.classPath);
    hash = 67 * hash + Objects.hashCode(this.message);
    return hash;
}

@Override
//lazy
public int hashCode() {
    return Objects.hash(id, timestamp, severity, thread, classPath, message);
}

虽然我不得不说这看起来好得令人难以置信。我也从未见过这种用法。

与实现自己的哈希码相比,使用Objects.hash()是否有任何缺点?我什么时候会选择这些方法?

更新

虽然此主题已标记为已解决,但请随时发布提供新信息和问题的答案。

5 个答案:

答案 0 :(得分:53)

请注意Objects.hash的参数为Object...。这有两个主要后果:

  • 哈希码计算中使用的原始值必须加框,例如this.id已从long转换为Long
  • 必须创建Object[]才能调用该方法。

创造这些"不必要的"如果频繁调用hashCode,对象可能会累加。

答案 1 :(得分:23)

以下是 Objects.hash 的实现 - 它在内部调用Arrays.hashCode。

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

这是 Arrays.hashCode 方法

的实现
public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

所以我同意@Andy 创造这些"不必要的"如果经常调用hashCode,则对象可能会相加。如果你自己实现它会更快。

答案 2 :(得分:9)

我想尝试为两者提出强有力的论据。

打开免责声明

对于此答案,Objects.hash()Objects.hashCode()以及执行此角色的任何库提供的任何功能都是可互换的。首先,我想说,使用Objects.hash()或者根本不使用静态对象函数。支持或反对此方法的任何参数都需要对编译后的代码进行假设,这些假设不能保证为真。 (例如,编译器优化器可能会将函数调用转换为内联调用,从而绕过额外的调用堆栈和对象分配。就像没有任何有用的循环一样,不要将其转换为编译版本(除非你转向关于优化器)。你也无法保证未来的Java版本不会像C#那样包含像这个方法版本的JVM版本。(出于安全考虑,我相信))所以唯一的安全论点关于使用这个函数,你可以把这个函数留下正确哈希的细节比试图实现你自己的朴素版本更安全。

For Objects.hash

  • 保证是一个好的哈希。
  • 需要5秒钟才能实施。
  • 你的内容会有一个错误(特别是如果你复制粘贴了实现)

针对Objects.hash

  • Java docs没有对散列交叉兼容性做出任何承诺(JVM v6和JVM v8是否会提供相同的值?总是?跨操作系统?)
  • 关于hashCodes的事情,如果"均匀分布"它们效果最好。因此,如果int值仅对范围1到100有效,您可能希望"重新分配"它的哈希码并不都是同一个桶的一部分。
  • 如果您有任何要求让您质疑Objects.hash如何工作,可靠性/性能明智,请仔细考虑哈希码是否真的符合您的要求,并实现一个自定义哈希编码方法来解决您的问题需求。

答案 3 :(得分:1)

约书亚·布洛赫(Joshua Bloch)在他的《有效的Java》(第3版)中,第7页。如果性能至关重要,则53不鼓励使用Objects.hash(...)

原语被自动装箱,创建Object数组会受到惩罚。

答案 4 :(得分:0)

我个人首先要使用短代码,因为这样可以更快地读取,更改和验证正确性,所有这些都可以避免在修改类时出现错误。

然后,对于性能至关重要的类或字段需要进行哈希运算的地方,我将缓存结果(就像String那样):

// volatile not required for 32-bit sized primitives
private int hash;

@Override
public final int hashCode() {
    // "Racy Single-check idiom" (Item 71, Effective Java 2nd ed.)
    int h = hash;
    if (h == 0) {
        h = Objects.hash(id, timestamp, severity, thread, classPath, message);
        hash = h;
    }
    return h;
}

在这种无锁线程安全模式(自然地假定一个不变的类)中,hash可能被不同线程多次初始化的可能性很小,但这无关紧要,因为public方法的结果是始终相同。 (内存可见性)正确性的关键是确保方法中不会hash多次写入和读取。

一旦代码由C2编译器进行JIT内联和优化,Objects.hash的数组构造代价可能会比您想象的要低。幸运的是,优秀的JDK专家已经完成了一些烘焙工作,从根本上消除了使用它的所有开销:JEP 348: Java Compiler Intrinsics for JDK APIs