国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

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

2019-11-08 03:00:20
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

Object 類是所有類的父類,其 equals 方法比較的是兩個(gè)對(duì)象的引用指向的地址,hashcode 是一個(gè)本地方法,返回的是對(duì)象地址值。他們都是通過(guò)比較地址來(lái)比較對(duì)象是否相等的。其實(shí)這兩個(gè)方法本身并沒(méi)有任何關(guān)聯(lián)。


那么,為什么重寫 equals方法的同時(shí)必須重寫 hashcode方法 ? 可以這樣理解:重寫了 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;}根據(jù) key,也就是 HashSet 所要添加的對(duì)象,得到 hashcode,由 hashcode 做特定位運(yùn)算得到 hash 碼;利用 hash 碼定位找到數(shù)組下標(biāo),得到鏈表的鏈?zhǔn)祝槐闅v鏈表尋找有沒(méi)有相同的 key,判斷依據(jù)是 e.hash == hash && ((k = e.key) == key || key.equals(k))。在add Lily 的時(shí)候,由于重寫了 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)有重寫 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

重寫了 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í)候,特定情況下重寫的 equals 方法比較對(duì)象是否相等的業(yè)務(wù)邏輯比較復(fù)雜,循環(huán)下來(lái)更是影響查找效率。所以這里把 hashcode 的判斷放在前面,只要 hashcode 不相等就玩兒完,不用再去調(diào)用復(fù)雜的 equals 了。很多程度地提升 HashMap 的使用效率。

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


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 固镇县| 淳安县| 桓台县| 连城县| 嫩江县| 高雄县| 宣城市| 城固县| 大姚县| 铜川市| 山东| 光山县| 南召县| 株洲市| 炎陵县| 右玉县| 大新县| 舟山市| 张北县| 凭祥市| 邯郸县| 眉山市| 贵南县| 温州市| 凤阳县| 洛宁县| 信宜市| 武隆县| 和平区| 建德市| 伊春市| 雅江县| 彩票| 崇信县| 化隆| 英吉沙县| 中山市| 祁东县| 金阳县| 同德县| 米脂县|