Java 使用Objects.hash()还是自己的hashCode()实现?
我最近发现了这个方法 我的第一个想法是,这会大大整理您的Java 使用Objects.hash()还是自己的hashCode()实现?,java,hash,Java,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 >&
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()
是否有任何缺点?我什么时候会选择这些方法中的每一种
更新
尽管此主题标记为已解决,但请随时发布提供新信息和关注点的答案。请注意
对象的参数。散列是对象…
。这有两个主要后果:
- 散列码计算中使用的原语值必须装箱,例如,
此.id
从long
转换为long
- 必须创建一个
对象[]
,以调用该方法
如果频繁调用hashCode
,则创建这些“不必要”对象的成本可能会增加。以下是对象的实现。hash-在内部调用Arrays.hashCode
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
这是数组.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版本。(出于安全原因,我相信))因此,关于使用此函数,您可以提出的唯一安全论点,通常,将适当哈希的细节留给此函数比尝试实现自己的原始版本更安全
对于Objects.hash
- 保证是一个好的散列
- 实施需要5秒钟
- 您的可能有一个bug(特别是如果您复制粘贴了实现)
针对Objects.hash
- Java没有承诺哈希交叉兼容性(JVMV6和JVMV8会给出相同的值吗?总是?跨操作系统?)
- 最重要的是,如果“均匀分布”,它们的效果最好。因此,如果一个int值仅对范围1到100有效,那么您可能希望“重新分配”其哈希代码,使其不都是同一个bucket的一部分
- 如果您有任何要求让您质疑Objects.hash如何工作,从可靠性/性能角度考虑,请仔细考虑hash代码是否真的是您想要的,并实现一种自定义hash编码方法,以满足您的需求
约书亚·布洛赫(Joshua Bloch)在其著作《有效的Java》第三版第。53如果性能至关重要,则不鼓励使用对象。哈希(…)
原语将被自动装箱,创建对象
数组将受到惩罚 就我个人而言,我会首先选择简短的代码,因为阅读、更改和验证代码要快得多,因为它们都是正确的,在修改类时可以避免错误
然后,对于性能关键类或散列成本较高的字段,我将缓存结果(如String
does):
在这种无锁线程安全模式中(它自然假设一个不可变的类),hash
可能会被不同的线程多次初始化,但这并不重要,因为public方法的结果总是相同的。(内存可见性)正确性的关键是确保方法中的hash
不会被多次写入和读取
一旦代码得到JIT内联并由C2编译器优化,则Objects.hash的数组构造代价可能会比您想象的要低。幸运的是,优秀的JDK人员有一些基本上可以消除使用它的所有开销的东西:另请参见HashCodeBulider
:但是commons builder使用反射。它很方便,但绝对是性能杀手。@NPE我真的想把它留给当地人。我不太喜欢外部apache common Stuff Use Lombok和@EqualsAndHashCode
@MartinSchröder,谢谢,但我想保持我的依赖关系。与所有性能问题一样,你应该向自己证明,你先关心后关心。@CandiedOrange yes;另一方面,不要编写您可能需要关心的代码。我的意思是,您可以使用返回0来实现hashCode
代码>如果你不关心性能,除非你已经证明不是这样。由于您只需编写一次,而且IDE很可能会为您生成代码,因此您最好选择详细但性能良好的版本。但我不会急于将现有代码中的任何一种形式更改为另一种形式。您在这里指的是对象。hashCode
:您的意思是这样做,还是指对象。hash
(这就是OP所问的)?@而且就我在这个答案中真正关心的问题而言,任何库提供的哈希函数都是可互换的,不会真正影响点。如果你真的在乎这两者之间的区别,你会怎么做
// 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;
}