我最近发现了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()
有什么缺点吗?我什么时候会选择这些方法?
更新
虽然此主题已标记为已解决,但您可以随时发布提供新信息和疑虑的答案。
请注意,Objects.hash
的参数是Object...
。这有两个主要后果:
this.id
从long
转换为Long
。Object[]
才能调用该方法。如果频繁调用hashCode
,则创建这些“不必要”对象的成本可能会增加。
以下是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,那么创建这些“不必要”对象的成本可能会增加。如果你自己实现它会更快。
我想尝试为两者做出强有力的论据。
对于这个答案,Objects.hash()
,Objects.hashCode()
以及执行此角色的任何库提供的任何功能都是可互换的。首先,我想说,使用Objects.hash()
或根本不使用静态对象函数。支持或反对此方法的任何参数都需要对编译后的代码进行假设,这些假设不能保证为真。 (例如,编译器优化器可能会将函数调用转换为内联调用,从而绕过额外的调用堆栈和对象分配。就像没有任何用处的循环没有进入编译版本一样(除非你关闭优化器)。你也不能保证未来的Java版本不会包含像C#那样的JVM版本。这个方法的版本也是如此。(出于安全考虑,我相信))所以你可以使用这个函数做出唯一安全的论证,将这个函数的正确哈希的细节留给试图实现你自己的天真版本通常更安全。
Joshua Bloch在他的着作Effective Java,第3版,p。 53如果表现很关键,则不鼓励使用Objects.hash(...)
。
基元正在自动生成,并且创建一个Object
阵列会受到惩罚。
就个人而言,我首先要使用短代码,因为它更快速地阅读,更改和验证是正确的,这些都可以在修改类时避免错误。
那么对于性能关键类或者字段的哈希成本很高,我会缓存结果(如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
可能会被不同的线程多次初始化,但这并不重要,因为公共方法的结果总是相同的。 (内存可见性)正确性的关键是确保hash
永远不会在方法中多次写入和读取。
一旦代码获得由C2编译器进行JIT内联和优化,Objects.hash
的数组构造损失可能低于您的想象。幸运的是,好的JDK人员有一些烘焙,基本上可以消除使用它的所有开销:JEP 348: Java Compiler Intrinsics for JDK APIs