equal和hashcode
equal和hashcode
核心代碼示例
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
{
return false;
}
Brand brand = (Brand) o;
return Objects.equals(name, brand.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
hashCode() 方法
- hashCode() 的作用是為對象生成一個數(shù)字“指紋”(哈希碼),這個“指紋”主要用于提高在基于哈希的集合(如 HashSet、HashMap)中的查找速度.
哈希碼
哈希碼是一個 int 類型的整數(shù)。理想情況下,相等的對象應(yīng)該擁有相同的哈希碼。不相等的對象,它們的哈希碼最好也不同(但這不是強制要求,只是為了讓性能更好)。
哈希碼原理
- 計算哈希碼: 當(dāng)你要添加一個新對象(比如 new Brand("BMW"))時,HashSet 首先調(diào)用這個對象的 hashCode() 方法,得到一個數(shù)字,比如 123。
- 定位“桶”: HashSet 內(nèi)部其實是一個數(shù)組,它用這個哈希碼(123)通過一個算法計算出這個對象應(yīng)該存放在數(shù)組的哪個位置(這個位置被稱為“桶”或“bucket”)。
快速判斷: - 比較: 如果這個“桶”是空的,說明這個對象肯定不存在,直接放進(jìn)去。整個過程連一次 equals() 調(diào)用都不需要;如果這個“桶”里已經(jīng)有對象了(這種情況叫“哈希沖突”),這時才需要啟動 equals() 方法,讓新對象和桶里的那幾個對象逐一比較,看是否真的相等。
equals()與hashcode()的聯(lián)系
- Java 規(guī)定,這兩個方法必須遵守一個非常重要的契約:
如果兩個對象通過 equals() 方法比較是相等的,那么調(diào)用這兩個對象中任意一個對象的hashCode() 方法都必須產(chǎn)生相同的整數(shù)結(jié)果。
反過來則不成立:如果兩個對象的 hashCode() 相同,它們不一定 equals()(這就是哈希沖突)。
所以在修改判斷標(biāo)準(zhǔn)時,equals和hashcode要共同重寫。
圖形展示
graph TD
subgraph "HashSet 內(nèi)部結(jié)構(gòu) (一個哈希表)"
direction LR
subgraph "內(nèi)部數(shù)組 (桶數(shù)組 Bucket Array)"
direction TB
B0["桶 0<br>Index: 0"]
B1["桶 1<br>Index: 1"]
B2["桶 2<br>Index: 2"]
B3["桶 3<br>Index: 3"]
B4["桶 4<br>Index: 4"]
B5["桶 5<br>Index: 5"]
B6["桶 6<br>Index: 6"]
Bn["..."]
end
subgraph "桶 2 中的內(nèi)容 (哈希沖突)"
direction TB
Node1["對象: new Brand('Apple')<br>hashCode: 102"]
Node2["對象: new Brand('Google')<br>hashCode: 286"]
Node3["對象: new Brand('Microsoft')<br>hashCode: 470"]
end
subgraph "桶 5 中的內(nèi)容 (鏈表結(jié)構(gòu))"
direction TB
Node4["對象: new Brand('Ford')<br>hashCode: 789"]
Node5["對象: new Brand('Audi')<br>hashCode: 789"]
end
subgraph "桶 6 中的內(nèi)容 (紅黑樹結(jié)構(gòu))"
direction TB
TreeRoot["(根節(jié)點)<br>new Brand('Toyota')"]
TreeLeft["(左子節(jié)點)<br>new Brand('BYD')"]
TreeRight["(右子節(jié)點)<br>new Brand('Honda']"]
TreeRoot --> TreeLeft
TreeRoot --> TreeRight
end
end
%% --- 連接關(guān)系 ---
B2 --> Node1
Node1 --> Node2
Node2 --> Node3
B5 --> Node4
Node4 --> Node5
B6 --> TreeRoot
%% --- 樣式定義 ---
classDef bucket fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000;
classDef object fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000;
classDef tree fill:#c8e6c9,stroke:#1b5e20,stroke-width:2px,color:#000;
classDef empty fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,stroke-dasharray: 5 5,color:#9e9e9e;
class B0,B1,B3,B4,B7,Bn bucket;
class B2,B5,B6 bucket;
class Node1,Node2,Node3,Node4,Node5 object;
class TreeRoot,TreeLeft,TreeRight tree;
class B0,B1,B3,B4,B7,Bn empty;
完整的示例代碼
點擊查看代碼
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine();
int n = Integer.parseInt(line);
List<Car> cars = new ArrayList<>();
List<Brand> brands = new ArrayList<>();
for(int i=0;i<n;i++) {
line = scanner.nextLine();
Brand brand = new Brand(line);
if(!brands.contains(brand)) {//應(yīng)用
brands.add(brand);
cars.add(new Car(brand));
}else {
System.out.println(line+" exists");
}
}
for(Car car:cars) {
System.out.println(car);
}
System.out.println(ArrayList.class.getClassLoader());
scanner.close();
}
}
class Brand{
private String name;
public Brand(String name) {
this.name = name;
}
public String toString() {
return "Brand [name="+name+"]";
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) //判斷相同類
{
return false;
}
Brand brand = (Brand) o;
return Objects.equals(name, brand.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
class Car{
int id;
Brand brand;
private static int counter=0;
static {
System.out.println("0 car");
}
public Car(Brand brand) {
this.brand = brand;
this.id = ++counter;
}
public String toString() {
return "Car [id="+id+"brand="+brand.toString()+"]";
}
}

浙公網(wǎng)安備 33010602011771號