欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

為什么在重寫(xiě) equals方法的同時(shí)必須重寫(xiě) hashcode方法

 更新時(shí)間:2016年07月27日 16:26:51   作者:屌絲的煩惱  
Object 類是所有類的父類,其 equals 方法比較的是兩個(gè)對(duì)象的引用指向的地址,hashcode 是一個(gè)本地方法,返回的是對(duì)象地址值。他們都是通過(guò)比較地址來(lái)比較對(duì)象是否相等的

我們都知道Java語(yǔ)言是完全面向?qū)ο蟮?,在java中,所有的對(duì)象都是繼承于Object類。
其 equals 方法比較的是兩個(gè)對(duì)象的引用指向的地址,hashcode 是一個(gè)本地方法,返回的是對(duì)象地址值。Ojbect類中有兩個(gè)方法equals、hashCode,這兩個(gè)方法都是用來(lái)比較兩個(gè)對(duì)象是否相等的。

為何重寫(xiě) equals方法的同時(shí)必須重寫(xiě) hashcode方法呢

可以這樣理解:重寫(xiě)了 equals 方法,判斷對(duì)象相等的業(yè)務(wù)邏輯就變了,類的設(shè)計(jì)者不希望通過(guò)比較內(nèi)存地址來(lái)比較兩個(gè)對(duì)象是否相等,而 hashcode 方法繼續(xù)按照地址去比較也沒(méi)有什么意義了,索性就跟著一起變吧。

還有一個(gè)原因來(lái)源于集合。下面慢慢說(shuō)~

舉個(gè)例子:

在學(xué)校中,是通過(guò)學(xué)號(hào)來(lái)判斷是不是這個(gè)人的。

下面代碼中情景為學(xué)籍錄入,學(xué)號(hào) 123 被指定給學(xué)生 Tom,學(xué)號(hào) 456 被指定給學(xué)生 Jerry,學(xué)號(hào) 123 被失誤指定給 Lily。而在錄入學(xué)籍的過(guò)程中是不應(yīng)該出現(xiàn)學(xué)號(hào)一樣的情況的。

根據(jù)情景需求是不能添加重復(fù)的對(duì)象,可以通過(guò) HashSet 實(shí)現(xiàn)。

public class Test {
public static void main(String[] args) {
Student stu = new Student(123,"Tom");
HashSet<Student> set = new HashSet<>();
set.add(stu);
set.add(new Student(456, "Jerry"));
set.add(new Student(123, "Lily"));
Iterator<Student> iterator = set.iterator();
while (iterator.hasNext()) {
Student student = iterator.next(); 
System.out.println(student.getStuNum() + " --- " + student.getName());
}
}
};
class Student {
private int stuNum;
private String name;
public Student(int stuNum,String name){
this.stuNum = stuNum;
this.name = name;
}
public int getStuNum() {
return stuNum;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if(this==obj)
return true;
if(obj instanceof Student){
if(this.getStuNum()==((Student)obj).getStuNum())
return true;
}
return false;
}
} 

輸出為:

123 --- Lily
456 --- Jerry
123 --- Tom

根據(jù)輸出我們發(fā)現(xiàn),再次將學(xué)號(hào) 123 指定給 Lily 居然成功了。到底哪里出了問(wèn)題呢?

我們看一下 HashSet 的 add 方法:

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

其實(shí) HashSet 是通過(guò) HashMap 實(shí)現(xiàn)的,由此我們追蹤到 HashMap 的 put 方法:

public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
} 

1.根據(jù) key,也就是 HashSet 所要添加的對(duì)象,得到 hashcode,由 hashcode 做特定位運(yùn)算得到 hash 碼;

2.利用 hash 碼定位找到數(shù)組下標(biāo),得到鏈表的鏈?zhǔn)祝?/p>

3.遍歷鏈表尋找有沒(méi)有相同的 key,判斷依據(jù)是 e.hash == hash && ((k = e.key) == key || key.equals(k))。在add Lily 的時(shí)候,由于重寫(xiě)了 equals 方法,遍歷到 Tom 的時(shí)候第二個(gè)條件應(yīng)該是 true;但是因?yàn)?hashcode 方法還是使用父類的,故而 Tom 和 Lily的 hashcode 不同也就是 hash 碼不同,第一個(gè)條件為 false。這里得到兩個(gè)對(duì)象是不同的所以 HashSet 添加 Lily 成功。

總結(jié)出來(lái)原因是沒(méi)有重寫(xiě) hashcode 方法,下面改造一下:

public class Test {
public static void main(String[] args) {
Student stu = new Student(123,"Tom");
HashSet<Student> set = new HashSet<>();
set.add(stu);
set.add(new Student(456, "Jerry"));
set.add(new Student(123, "Lily"));
Iterator<Student> iterator = set.iterator();
while (iterator.hasNext()) {
Student student = iterator.next(); 
System.out.println(student.getStuNum() + " --- " + student.getName());
}
}
};
class Student {
private int stuNum;
private String name;
public Student(int stuNum,String name){
this.stuNum = stuNum;
this.name = name;
}
public int getStuNum() {
return stuNum;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if(this==obj)
return true;
if(obj instanceof Student){
if(this.getStuNum()==((Student)obj).getStuNum())
return true;
}
return false;
}
@Override
public int hashCode() {
return getStuNum();
}
} 

輸出:

456 --- Jerry
123 --- Tom

重寫(xiě)了 hashcode 方法返回學(xué)號(hào)。OK,大功告成。

有人可能會(huì)奇怪,e.hash == hash && ((k = e.key) == key || key.equals(k)) 這個(gè)條件是不是有點(diǎn)復(fù)雜了,我感覺(jué)只使用 equals 方法就可以了啊,為什么要多此一舉去判斷 hashcode 呢?

因?yàn)樵?HashMap 的鏈表結(jié)構(gòu)中遍歷判斷的時(shí)候,特定情況下重寫(xiě)的 equals 方法比較對(duì)象是否相等的業(yè)務(wù)邏輯比較復(fù)雜,循環(huán)下來(lái)更是影響查找效率。所以這里把 hashcode 的判斷放在前面,只要 hashcode 不相等就玩兒完,不用再去調(diào)用復(fù)雜的 equals 了。很多程度地提升 HashMap 的使用效率。

所以重寫(xiě) hashcode 方法是為了讓我們能夠正常使用 HashMap 等集合類,因?yàn)?HashMap 判斷對(duì)象是否相等既要比較 hashcode 又要使用 equals 比較。而這樣的實(shí)現(xiàn)是為了提高 HashMap 的效率。

相關(guān)文章

  • 一文帶你了解RabbitMQ消息轉(zhuǎn)換器

    一文帶你了解RabbitMQ消息轉(zhuǎn)換器

    這篇文章主要為大家詳細(xì)介紹了RabbitMQ中消息轉(zhuǎn)換器的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下
    2023-04-04
  • Java實(shí)現(xiàn)Json字符串與Object對(duì)象相互轉(zhuǎn)換的方式總結(jié)

    Java實(shí)現(xiàn)Json字符串與Object對(duì)象相互轉(zhuǎn)換的方式總結(jié)

    這篇文章主要介紹了Java實(shí)現(xiàn)Json字符串與Object對(duì)象相互轉(zhuǎn)換的方式,結(jié)合實(shí)例形式總結(jié)分析了java基于Json-Lib、Org.Json、Jackson、Gson、FastJson五種方式轉(zhuǎn)換json類型相關(guān)操作技巧,需要的朋友可以參考下
    2019-03-03
  • 詳解基于java的Socket聊天程序——初始設(shè)計(jì)(附demo)

    詳解基于java的Socket聊天程序——初始設(shè)計(jì)(附demo)

    本篇文章主要介紹了Socket聊天程序——初始設(shè)計(jì)(附demo),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-12-12
  • RocketMQ線程池創(chuàng)建實(shí)現(xiàn)原理詳解

    RocketMQ線程池創(chuàng)建實(shí)現(xiàn)原理詳解

    這篇文章主要為大家介紹了RocketMQ線程池創(chuàng)建實(shí)現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Spring常用數(shù)據(jù)源的xml配置詳解

    Spring常用數(shù)據(jù)源的xml配置詳解

    這篇文章主要介紹了Spring常用數(shù)據(jù)源的xml配置詳解,數(shù)據(jù)源是連接到數(shù)據(jù)庫(kù)的一類路徑,它包含了訪問(wèn)數(shù)據(jù)庫(kù)的信息(地址、用戶名、密碼),數(shù)據(jù)源就像是排水管道,需要的朋友可以參考下
    2023-07-07
  • mybatis源碼解讀-Java中executor包的語(yǔ)句處理功能

    mybatis源碼解讀-Java中executor包的語(yǔ)句處理功能

    這篇文章主要介紹了Java中executor包的語(yǔ)句處理功能,在mybatis映射文件中傳參數(shù),主要用到#{}或者${},下文圍繞相關(guān)資料展開(kāi)詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-02-02
  • 關(guān)于在Java中使用預(yù)定義類

    關(guān)于在Java中使用預(yù)定義類

    這篇文章主要介紹了關(guān)于在Java中使用預(yù)定義類,預(yù)定義類就是Java類庫(kù)(或第三方庫(kù))中已經(jīng)定義好的類,例如,Math 類和 Date 類,需要的朋友可以參考下
    2023-05-05
  • Java獲取當(dāng)?shù)氐娜粘鋈章鋾r(shí)間代碼分享

    Java獲取當(dāng)?shù)氐娜粘鋈章鋾r(shí)間代碼分享

    這篇文章主要介紹了Java獲取當(dāng)?shù)氐娜粘鋈章鋾r(shí)間代碼分享,國(guó)外猿友寫(xiě)的一個(gè)類,需要的朋友可以參考下
    2014-06-06
  • java客戶端線上Apollo服務(wù)端的實(shí)現(xiàn)

    java客戶端線上Apollo服務(wù)端的實(shí)現(xiàn)

    這篇文章主要介紹了java客戶端線上Apollo服務(wù)端的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • J2EE驗(yàn)證碼圖片如何生成和點(diǎn)擊刷新驗(yàn)證碼

    J2EE驗(yàn)證碼圖片如何生成和點(diǎn)擊刷新驗(yàn)證碼

    這篇文章主要介紹了J2EE如何生成驗(yàn)證碼圖片如何生成,如何點(diǎn)擊刷新驗(yàn)證碼的相關(guān)方法,感興趣的小伙伴們可以參考一下
    2016-04-04

最新評(píng)論