哈希原理
首先,在我們定義Java哈希碼之前,我們需要了解什麼是哈希以及它有什麼用。哈希是將哈希函數應用於某些數據的過程。哈希函數只是一個數學函數。別擔心這個!“數學”並不總是意味著“複雜”。這裡只表示我們有一些數據和將數據映射成一組字符(代碼)的某種規則。例如,它可以是十六進制密碼。我們在輸入端有一些任意大小的數據,並對其應用哈希函數。在輸出中,我們得到一個固定大小的數據,比如 32 個字符。通常,這種函數將一大段數據轉換成一個小的整數值。該函數工作的結果稱為哈希碼。哈希函數廣泛用於密碼學和其他一些領域。哈希函數可以不同,
- 特定對象具有特定的哈希碼。
- 如果兩個對象相等,則它們的哈希碼相同。反之則不然。
- 如果哈希碼不同,則對象肯定不相等。
- 不同的對象可能具有相同的哈希碼。然而,這是一個極不可能發生的事件。在這一點上,我們有一個碰撞,一個我們可能會丟失數據的情況。
“適當的”散列函數將衝突的可能性降到最低。
Java 中的哈希碼
在 Java 中,哈希函數通常連接到
hashCode() 方法。準確地說,將哈希函數應用於對象的結果是哈希碼。每個 Java 對像都有一個哈希碼。一般來說,Hash Code 是通過類的
hashCode() 方法計算出來的一個數字
Object
。通常,程序員會為他們的對象重寫此方法以及與
hashCode()相關的equals
()方法,以便更有效地處理特定數據。hashCode
()方法返回一個 int(4 字節)值,它是對象的數字表示。例如,集合使用此哈希碼來更有效地存儲數據,並相應地更快地訪問它們。默認情況下,
hashCode()對象的函數返回存儲對象的內存單元的編號。因此,如果不對應用程序代碼進行更改,則該函數應返回相同的值。如果代碼稍有變化,哈希碼值也會發生變化。Java中使用的哈希碼是什麼?首先,Java 哈希碼幫助程序運行得更快。例如,如果我們比較兩個對象
o1
和
o2
某種類型,該操作
o1.equals(o2)
所花費的時間大約是 的 20 倍
o1.hashCode() == o2.hashCode()
。
Java 等於()
在父類中
Object
,除了
hashCode()方法之外,還有
equals(),用於檢查兩個對像是否相等的函數。此函數的默認實現只是檢查兩個對象的鏈接是否等價。
equals()和
hashCode()有它們的契約,所以如果你覆蓋其中一個,你應該覆蓋另一個,以免破壞這個契約。
實現 hashCode() 方法
例子
讓我們用一個字段創建一個類
Character — name。之後,我們創建兩個
Character類的對象
character1和
character2並設置它們相同的名稱。
如果我們使用Object類的默認
hashCode()和
equals(),我們肯定會得到不同的,而不是相等的對象。Java 中的 hashcode 就是這樣工作的。它們將具有不同的哈希碼,因為它們位於不同的內存單元中,並且
equals()操作結果將為 false。
import java.util.Objects;
public class Character {
private String Name;
public Character(String name) {
Name = name;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public static void main(String[] args) {
Character character1 = new Character("Arnold");
System.out.println(character1.getName());
System.out.println(character1.hashCode());
Character character2 = new Character("Arnold");
System.out.println(character2.getName());
System.out.println(character2.hashCode());
System.out.println(character2.equals(character1));
}
}
程序運行結果:
Arnold
1595428806
Arnold
1072408673
false
控制台中的兩個 10 位數字是哈希碼。如果我們想要擁有相同名稱的相同對象怎麼辦?我們應該做什麼?答案:我們應該為我們的
Character類覆蓋Object類的hashCode()和
equals()方法。我們可以在 IDEA IDE 中自動完成,只需按鍵盤上的
alt + insert並選擇Generate -> equals() and hashCode()。 在我們的示例中,我們有下一個代碼:
import java.util.Objects;
public class Character {
private String Name;
public Character(String name) {
Name = name;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Character)) return false;
Character character = (Character) o;
return getName() != null ? getName().equals(character.getName()) : character.getName() == null;
}
@Override
public int hashCode() {
return getName() != null ? getName().hashCode() : 0;
}
public static void main(String[] args) {
Character character1 = new Character("Arnold");
System.out.println(character1.getName());
System.out.println(character1.hashCode());
Character character2 = new Character("Arnold");
System.out.println(character2.getName());
System.out.println(character2.hashCode());
System.out.println(character2.equals(character1));
}
}
運行這段代碼的結果:
Arnold
1969563338
Arnold
1969563338
true
所以現在程序將我們的對象識別為相等的,並且它們具有相同的哈希碼。
Java 哈希碼示例:
你自己的 hashCode() 和 equals()
您也可以創建自己的
equals()和
hashCode()實現,但要小心並記住盡量減少哈希碼衝突。
這是我們在Student類中自己的hashCode()和
equals()方法的示例:
import java.util.Date;
public class Student {
String surname;
String name;
String secondName;
Long birthday; // Long instead of long is used by Gson/Jackson json parsers and various orm databases
public Student(String surname, String name, String secondName, Date birthday ){
this.surname = surname;
this.name = name;
this.secondName = secondName;
this.birthday = birthday == null ? 0 : birthday.getTime();
}
//Java hashcode example
@Override
public int hashCode(){
//TODO: check for nulls
//return surname.hashCode() ^ name.hashCode() ^ secondName.hashCode() ^ (birthday.hashCode());
return (surname + name + secondName + birthday).hashCode();
}
@Override
public boolean equals(Object other_) {
Student other = (Student)other_;
return (surname == null || surname.equals(other.surname) )
&& (name == null || name.equals(other.name))
&& (secondName == null || secondName.equals(other.secondName))
&& (birthday == null || birthday.equals(other.birthday));
}
}
和
主類來展示他們的工作:
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
public class Main {
static HashMap<Student, Integer> cache = new HashMap<Student, Integer>(); // <person, targetPriority>
public static void main(String[] args) {
Student sarah1 = new Student("Sarah","Connor", "Jane", null);
Student sarah2 = new Student("Sarah","Connor", "Jane", new Date(1970, 01-1, 01));
Student sarah3 = new Student("Sarah","Connor", "Jane", new Date(1959, 02-1, 28)); // date not exists
Student john = new Student("John","Connor", "Kyle", new Date(1985, 02-1, 28)); // date not exists
Student johnny = new Student("John","Connor", "Kyle", new Date(1985, 02-1, 28)); // date not exists
System.out.println(john.hashCode());
System.out.println(johnny.hashCode());
System.out.println(sarah1.hashCode());
System.out.println();
cache.put(sarah1, 1);
cache.put(sarah2, 2);
cache.put(sarah3, 3);
System.out.println(new Date(sarah1.birthday));
System.out.println();
cache.put(john, 5);
System.out.println(cache.get(john));
System.out.println(cache.get(johnny));
cache.put(johnny, 7);
System.out.println(cache.get(john));
System.out.println(cache.get(johnny));
}
}
哈希碼有什麼用?
首先,哈希碼幫助程序運行得更快。例如,如果我們比較兩個對象
o1
和
o2
某種類型,該操作
o1.equals(o2)
比 o1.hashCode() == o2.hashCode() 花費大約 20 倍的時間。在 Java 中,散列原理支持一些流行的集合,例如
HashMap、
HashSet和
HashTable。
結論
每個 Java 對像都有繼承自
Object類的hashCode()和
equals()方法。要獲得良好的工作平等機制,您最好為您自己的類覆蓋
hashcode()和
equals()方法。使用哈希碼可以使程序運行得更快。
GO TO FULL VERSION