[Java] hashCode 方法的记事本
要搞清楚要达到的目标
-
- Javaで hashCode 関連のメソッドはいくつかのクラスに分散しているので、整理してみます。
- 各メソッドの詳細な説明はしません。
哈希码的基础
-
- ハッシュ値を求める。ハッシュテーブル探索などで使われる(HashMap や HashSet など)
equals() とセットで、矛盾なく実装する必要がある
equals() が true を返すオブジェクトは、同じハッシュ値であること
違うハッシュ値である場合には、equals() は false を返すこと(対偶)
equals() が false を返すオブジェクトが、同じハッシュ値であっても構わない(ハッシュ値の衝突)が、衝突が少ないほうが性能が良い
方法
java.lang.Object#hashCode()可以用以下方式进行概括:
当想要将自定义的类用作HashMap等的键时,需要自行实现计算实例的哈希值。
java.util.Objects#hash() 的中文释义。
计算通过可变参数传递的对象的哈希值。实际上,它相当于调用后文提到的 Arrays#hashCode() 方法。
java.util.Objects类的hashCode()方法
计算指定对象的哈希值。即使传入 null,也不会发生错误。
java.util.Arrays#hashCode() 的中文释义如下:将数组转换为哈希码。
在中国的一种选择性的版本中,重新述说如下:
数组对象的hashCode()方法会返回与Object类相同的结果(即在计算过程中不使用数组的值)。
如果想要根据数组的内容计算哈希值,请使用 Arrays#hashCode()。
即使传入 null,也不会报错。
然而,多维数组等可能不会得到预期的计算结果。
如果需要,请使用 Arrays#deepHashCode()。
另外,在某些情况下,可以只对数组的部分进行哈希计算(例如,只保留了容量但尚未填入数据等)。请自行计算。
Java.util.Arrays的deepHashCode()方法
求取嵌套数组的哈希值。
最好不要传递自我循环引用的数组(可能会导致无限循环或堆栈溢出)。
传递 null 不会导致错误。
java.lang.Integer#hashCode()的原生中文解释。
有一个名字相同的实例方法来计算Integer类型的哈希值,以及一个类静态方法来计算int类型的哈希值。
Integer i = getInteger();
int hash1 = i.hashCode();
int hash2 = Integer.hashCode(1234);
另外,除了Integer外,其他原始类型的包装类也有类似的方法。
java.lang.System的identityHashCode()函数
以与 Object 相同的哈希值计算方法,找出指定对象的哈希值。即使对象具有自定义的哈希计算方式也可以。
為了在一定程度上識別實例,有時會在日誌輸出或toString()生成的字串中加入額外資訊。
即使是不同的實例,也可能具有相同的哈希值,因此僅供參考。
计算方式(实施方法)
手動計算雜湊
通常情况下,我们经常使用应该用于哈希计算的字段值进行计算。
@Override
public int hashCode()
int result = 17;
result *= 31;
result += フィールド1のハッシュ値;
result *= 31;
result += フィールド2のハッシュ値;
result *= 31;
result += フィールド3のハッシュ値;
return result;
}
另外,对于字段的哈希值计算方法(例如),可以采用以下方式。
int の値はそのまま
byte, char, short の値は int に変換して
boolean は真が 1、偽が 0
long の値は上位 32bit と下位 32bit に分割して、ビット単位の xor で int に変換
float は Float#floatToIntBits() で int に変換
double は Double#doubleToLongBits() で long に変換して、あとは long と同じ
その他のオブジェクトの場合にはオブジェクトの hashCode() を呼び出して(あるいは実装されていない場合には、同じようにがんばる)
这里对《Effective Java第2版》非常了解。
父类的 hashCode() 方法
如果可以获取到父类字段的值, 那么在子类中也可以计算所有的值,但通常会调用父类的hashCode()方法来考虑其值。
但是,如果父类没有实现hashCode()方法(仍然是Object类的hashCode()),则不应该使用父类的hashCode()结果。
利用Java7的实用方法
由于Java7新增了方便的方法,因此通常情况下可以使用简单的代码来计算哈希值。
@Override
public int hashCode()
return Objects.hash(フィールド1,フィールド2,フィールド3);
}
使用IDE进行自动化生成
例如,在Eclipse中,您可以通过选择菜单中的 “Source” → “Generate hashCode() and equals() …” 来自动生成代码。
在Lombok中进行哈希计算。
如果给@Data注解、@Value注解或@EqualsAndHashCode注解,可以吗?
其他
URL 的哈希码()
好像有这样的故事。
- java.net.URLの闇
在计算URL的hashCode()时,需要对主机名进行地址解析,因此根据环境和时机可能会导致哈希值发生变化。
也许应该通过URI而不是URL来进行判断吧。
字符串的 hashCode()
这似乎是一个古老的故事。
- String.hashCode() の変遷
在Java的某个中间版本中,有关重新评估哈希计算方法的讨论。
计算标准哈希值的方法
在中国的本地语言中进行同义转述:标准(java.lang.Object)哈希值的计算方法似乎可以在虚拟机启动时进行更改。另外,据说在Java7及之前和Java8中,默认值有所不同。
- Java8 で java.lang.Object#hashCode() の生成アルゴリズムが変更されていました。
我在Windows上试着运行了以下命令。
c:\> java -XX:+PrintFlagsFinal -version
(中略)
intx hashCode = 5 {product}
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) Client VM (build 25.65-b01, mixed mode)
没错,hashCode 的值确实是 5。
在JDK1.7中,同样地,检查hashCode 的结果为0。