為何在重寫 equals辦法的同時必需重寫 hashcode辦法。本站提示廣大學習愛好者:(為何在重寫 equals辦法的同時必需重寫 hashcode辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是為何在重寫 equals辦法的同時必需重寫 hashcode辦法正文
我們都曉得Java說話是完整面向對象的,在java中,一切的對象都是繼續於Object類。
其 equals 辦法比擬的是兩個對象的援用指向的地址,hashcode 是一個當地辦法,前往的是對象地址值。Ojbect類中有兩個辦法equals、hashCode,這兩個辦法都是用來比擬兩個對象能否相等的。
為什麼重寫 equals辦法的同時必需重寫 hashcode辦法呢
可以如許懂得:重寫了 equals 辦法,斷定對象相等的營業邏輯就變了,類的設計者不願望經由過程比擬內存地址來比擬兩個對象能否相等,而 hashcode 辦法持續依照地址去比擬也沒有甚麼意義了,索性就隨著一路變吧。
還有一個緣由起源於聚集。上面漸漸說~
舉個例子:
在黉捨中,是經由過程學號來斷定是否是這小我的。
上面代碼中情形為學籍錄入,學號 123 被指定給先生 Tom,學號 456 被指定給先生 Jerry,學號 123 被掉誤指定給 Lily。而在錄退學籍的進程中是不該該湧現學號一樣的情形的。
依據情形需求是不克不及添減輕復的對象,可以經由過程 HashSet 完成。
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
依據輸入我們發明,再次將學號 123 指定給 Lily 竟然勝利了。究竟哪裡出了成績呢?
我們看一下 HashSet 的 add 辦法:
public boolean add(E e) { return map.put(e, PRESENT)==null; }
其實 HashSet 是經由過程 HashMap 完成的,由此我們追蹤到 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.依據 key,也就是 HashSet 所要添加的對象,獲得 hashcode,由 hashcode 做特定位運算獲得 hash 碼;
2.應用 hash 碼定位找到數組下標,獲得鏈表的鏈首;
3.遍歷鏈表尋覓有無雷同的 key,斷定根據是 e.hash == hash && ((k = e.key) == key || key.equals(k))。在add Lily 的時刻,因為重寫了 equals 辦法,遍歷到 Tom 的時刻第二個前提應當是 true;然則由於 hashcode 辦法照樣應用父類的,故而 Tom 和 Lily的 hashcode 分歧也就是 hash 碼分歧,第一個前提為 false。這裡獲得兩個對象是分歧的所以 HashSet 添加 Lily 勝利。
總結出來緣由是沒有重寫 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 辦法前往學號。OK,年夜功樂成。
有人能夠會奇異,e.hash == hash && ((k = e.key) == key || key.equals(k)) 這個前提是否是有點龐雜了,我感到只應用 equals 辦法便可以了啊,為何要畫蛇添足去斷定 hashcode 呢?
由於在 HashMap 的鏈表構造中遍歷斷定的時刻,特定情形下重寫的 equals 辦法比擬對象能否相等的營業邏輯比擬龐雜,輪回上去更是影響查找效力。所以這裡把 hashcode 的斷定放在後面,只需 hashcode 不相等就玩兒完,不消再去挪用龐雜的 equals 了。許多水平地晉升 HashMap 的應用效力。
所以重寫 hashcode 辦法是為了讓我們可以或許正常應用 HashMap 等聚集類,由於 HashMap 斷定對象能否相等既要比擬 hashcode 又要應用 equals 比擬。而如許的完成是為了進步 HashMap 的效力。