mirror of
https://github.com/sjsdfg/effective-java-3rd-chinese.git
synced 2024-12-28 05:40:45 +08:00
update 11. 重写 equals 方法时同时也要重写 hashcode 方法
This commit is contained in:
parent
f2e2a16d79
commit
d4acbf6209
@ -12,7 +12,6 @@
|
||||
|
||||
```java
|
||||
Map<PhoneNumber, String> m = new HashMap<>();
|
||||
|
||||
m.put(new PhoneNumber(707, 867, 5309), "Jenny");
|
||||
```
|
||||
|
||||
@ -23,7 +22,6 @@ m.put(new PhoneNumber(707, 867, 5309), "Jenny");
|
||||
|
||||
```java
|
||||
// The worst possible legal hashCode implementation - never use!
|
||||
|
||||
@Override public int hashCode() { return 42; }
|
||||
```
|
||||
|
||||
@ -37,7 +35,7 @@ m.put(new PhoneNumber(707, 867, 5309), "Jenny");
|
||||
|
||||
-- i. 如果这个属性是基本类型的,使用 `Type.hashCode(f)` 方法计算,其中 `Type` 类是对应属性 `f` 基本类型的包装类。<br/>
|
||||
-- ii. 如果该属性是一个对象引用,并且该类的 equals 方法通过递归调用 equals 来比较该属性,并递归地调用 hashCode 方法。 如果需要更复杂的比较,则计算此字段的“范式(“canonical representation)”,并在范式上调用 hashCode。 如果该字段的值为空,则使用 0(也可以使用其他常数,但通常来使用 0 表示)。<br/>
|
||||
-- iii. 如果属性 `f` 是一个数组,把它看作每个重要的元素都是一个独立的属性。 也就是说,通过递归地应用这些规则计算每个重要元素的哈希码,并且将每个步骤 2.b 的值合并。 如果数组没有重要的元素,则使用一个常量,最好不要为 0。如果所有元素都很重要,则使用 `Arrays.hashCode` 方法。<br/>
|
||||
-- iii. 如果属性 `f` 是一个数组,把它看作每个重要的元素都是一个独立的属性。 也就是说,通过递归地应用这些规则计算每个重要元素的哈希码,并且将每个步骤 2.b 的值合并。 如果数组没有重要的元素,则使用一个常量,最好不要为 0。如果所有元素都很重要,则使用 `Arrays.hashCode` 方法。<br/>
|
||||
|
||||
b. 将步骤 2.a 中属性 c 计算出的哈希码合并为如下结果:`result = 31 * result + c;`
|
||||
|
||||
@ -53,18 +51,12 @@ m.put(new PhoneNumber(707, 867, 5309), "Jenny");
|
||||
|
||||
```java
|
||||
// Typical hashCode method
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = Short.hashCode(areaCode);
|
||||
|
||||
result = 31 * result + Short.hashCode(prefix);
|
||||
|
||||
result = 31 * result + Short.hashCode(lineNum);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@ -77,12 +69,9 @@ public int hashCode() {
|
||||
|
||||
```java
|
||||
// One-line hashCode method - mediocre performance
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return Objects.hash(lineNum, prefix, areaCode);
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@ -90,28 +79,18 @@ public int hashCode() {
|
||||
|
||||
```java
|
||||
// hashCode method with lazily initialized cached hash code
|
||||
|
||||
private int hashCode; // Automatically initialized to 0
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = hashCode;
|
||||
|
||||
if (result == 0) {
|
||||
|
||||
result = Short.hashCode(areaCode);
|
||||
|
||||
result = 31 * result + Short.hashCode(prefix);
|
||||
|
||||
result = 31 * result + Short.hashCode(lineNum);
|
||||
|
||||
hashCode = result;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@ -122,7 +101,7 @@ public int hashCode() {
|
||||
**不要为 hashCode 返回的值提供详细的规范,因此客户端不能合理地依赖它; 你可以改变它的灵活性。** Java 类库中的许多类(例如 String 和 Integer)都将 hashCode 方法返回的确切值指定为实例值的函数。 这不是一个好主意,而是一个我们不得不忍受的错误:它妨碍了在未来版本中改进哈希函数的能力。 如果未指定细节并在散列函数中发现缺陷,或者发现了更好的哈希函数,则可以在后续版本中对其进行更改。
|
||||
|
||||
总之,每次重写 equals 方法时都必须重写 hashCode 方法,否则程序将无法正常运行。你的 hashCode 方法必须遵从 Object 类指定的常规约定,并且必须执行合理的工作,将不相等的哈希码分配给不相等的实例。如果使用第 51 页的配方,这很容易实现。如条目 10 所述,AutoValue 框架为手动编写 equals 和 hashCode 方法提供了一个很好的选择,IDE 也提供了一些这样的功能。
|
||||
|
||||
|
||||
|
||||
[1]: http://com.google.common.hash.hashing/
|
||||
|
||||
[1]: http://com.google.common.hash.hashing/
|
||||
|
Loading…
Reference in New Issue
Block a user