IntelliJ 默认 hashCode() 实现的解释
Explaination for IntelliJ default hashCode() implementation
我查看了 IntelliJ 默认的 hashCode()
实现,想知道为什么他们以这种方式实现它。我对哈希概念很陌生,发现了一些矛盾的陈述,需要澄清:
public int hashCode(){
// creationDate is of type Date
int result = this.creationDate != null ? this.creationDate.hashCode() : 0;
// id is of type Long (wrapper class)
result = 31 * result + (this.id != null ? this.id.hashCode() : 0);
// code is of type String
result = 31 * result + (this.code != null ? this.code.hashCode() : 0);
// revision is of type int
result = 31 * result + this.revision;
return result;
}
Imo,关于这个主题的最佳来源似乎是 this Java world article,因为我发现他们的论点最有说服力。所以我想知道:
- 除其他论点外,上述来源指出乘法是较慢的运算之一。那么,每当我调用引用类型的
hashCode()
方法时,跳过与质数的乘法不是更好吗?因为大多数时候这已经包含了这样的乘法。
- Java 世界声明按位异或
^
由于未提及的原因也改进了计算:(与常规加法相比究竟有什么优势?
- 当相应的 class 字段为
null
时,return 不同的值不是更好吗?它会使结果更容易区分,不是吗?使用非 zero
值是否有任何巨大的缺点?
他们的示例代码看起来更吸引我的眼球,说实话:
public boolean hashCode() {
return
(name == null ? 17 : name.hashCode()) ^
(birth == null ? 31 : name.hashCode());
}
但我不确定这是否客观真实。我对 IntelliJ 也有点怀疑,因为它们 equals(Object)
的默认代码通过 instanceof
进行比较,而不是直接比较实例 classes。我同意 Java 世界文章,这似乎没有正确履行合同。
至于 hashCode()
,我认为减少冲突(两个不同的对象具有相同的 hashCode())比 hashCode() 计算的速度更重要。是的,hashCode() 应该很快(如果可能的话,恒定时间),但是对于使用 hashCode() 的大型数据结构(映射、集合等),冲突是更重要的因素。
如果您的 hashCode() 函数在恒定时间内执行(与数据和输入大小无关)并产生良好的散列函数(很少发生冲突),则 map 上的操作(get、contains、put)将以恒定时间渐进地执行时间。
如果您的 hashCode() 函数产生大量冲突,性能将受到影响。在极端情况下,您始终可以从 hashCode() return 0
- 函数本身将超快,但地图操作将在线性时间内执行(即随着地图大小增长)。
在添加另一个字段的子 hashCode 之前乘以 hashCode() 通常应该提供较少的冲突 - 这是一种基于字段通常包含相似数据/小数字的启发式算法。
考虑 class 人的例子:
class Person {
int age;
int heightCm;
int weightKg;
}
如果您只是将数字相加来计算哈希码,则所有人的结果将介于 60 到 500 之间。如果你按照 Idea 的方式乘以它,你将得到 2000 到 100000 之间的 hashCodes - 更大 space 因此冲突的可能性更低。
使用 XOR 不是一个好主意,例如,如果您有 class Rectangle
字段 height
和 width
,所有方块将具有相同的 hashCode - 0
.
至于 equals()
使用 instanceof
与 getClass().equals()
,我从未见过关于此的定论。两者各有优缺点,一不小心就会出事:
- 如果您使用
instanceof
,任何覆盖您的 equals()
的子class 都可能会破坏对称性要求
- 如果您使用
getClass().equals()
,这将不适用于一些框架,例如 Hibernate,它们生成自己的 classes 的子classes 来存储自己的技术信息
我查看了 IntelliJ 默认的 hashCode()
实现,想知道为什么他们以这种方式实现它。我对哈希概念很陌生,发现了一些矛盾的陈述,需要澄清:
public int hashCode(){
// creationDate is of type Date
int result = this.creationDate != null ? this.creationDate.hashCode() : 0;
// id is of type Long (wrapper class)
result = 31 * result + (this.id != null ? this.id.hashCode() : 0);
// code is of type String
result = 31 * result + (this.code != null ? this.code.hashCode() : 0);
// revision is of type int
result = 31 * result + this.revision;
return result;
}
Imo,关于这个主题的最佳来源似乎是 this Java world article,因为我发现他们的论点最有说服力。所以我想知道:
- 除其他论点外,上述来源指出乘法是较慢的运算之一。那么,每当我调用引用类型的
hashCode()
方法时,跳过与质数的乘法不是更好吗?因为大多数时候这已经包含了这样的乘法。 - Java 世界声明按位异或
^
由于未提及的原因也改进了计算:(与常规加法相比究竟有什么优势? - 当相应的 class 字段为
null
时,return 不同的值不是更好吗?它会使结果更容易区分,不是吗?使用非zero
值是否有任何巨大的缺点?
他们的示例代码看起来更吸引我的眼球,说实话:
public boolean hashCode() {
return
(name == null ? 17 : name.hashCode()) ^
(birth == null ? 31 : name.hashCode());
}
但我不确定这是否客观真实。我对 IntelliJ 也有点怀疑,因为它们 equals(Object)
的默认代码通过 instanceof
进行比较,而不是直接比较实例 classes。我同意 Java 世界文章,这似乎没有正确履行合同。
至于 hashCode()
,我认为减少冲突(两个不同的对象具有相同的 hashCode())比 hashCode() 计算的速度更重要。是的,hashCode() 应该很快(如果可能的话,恒定时间),但是对于使用 hashCode() 的大型数据结构(映射、集合等),冲突是更重要的因素。
如果您的 hashCode() 函数在恒定时间内执行(与数据和输入大小无关)并产生良好的散列函数(很少发生冲突),则 map 上的操作(get、contains、put)将以恒定时间渐进地执行时间。
如果您的 hashCode() 函数产生大量冲突,性能将受到影响。在极端情况下,您始终可以从 hashCode() return 0
- 函数本身将超快,但地图操作将在线性时间内执行(即随着地图大小增长)。
在添加另一个字段的子 hashCode 之前乘以 hashCode() 通常应该提供较少的冲突 - 这是一种基于字段通常包含相似数据/小数字的启发式算法。
考虑 class 人的例子:
class Person {
int age;
int heightCm;
int weightKg;
}
如果您只是将数字相加来计算哈希码,则所有人的结果将介于 60 到 500 之间。如果你按照 Idea 的方式乘以它,你将得到 2000 到 100000 之间的 hashCodes - 更大 space 因此冲突的可能性更低。
使用 XOR 不是一个好主意,例如,如果您有 class Rectangle
字段 height
和 width
,所有方块将具有相同的 hashCode - 0
.
至于 equals()
使用 instanceof
与 getClass().equals()
,我从未见过关于此的定论。两者各有优缺点,一不小心就会出事:
- 如果您使用
instanceof
,任何覆盖您的equals()
的子class 都可能会破坏对称性要求 - 如果您使用
getClass().equals()
,这将不适用于一些框架,例如 Hibernate,它们生成自己的 classes 的子classes 来存储自己的技术信息