一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 為什么在重寫 equals方法的同時必須重寫 hashcode方法

為什么在重寫 equals方法的同時必須重寫 hashcode方法

2020-06-01 13:31屌絲的煩惱 JAVA教程

Object 類是所有類的父類,其 equals 方法比較的是兩個對象的引用指向的地址,hashcode 是一個本地方法,返回的是對象地址值。他們都是通過比較地址來比較對象是否相等的

我們都知道Java語言是完全面向對象的,在java中,所有的對象都是繼承于Object類。
其 equals 方法比較的是兩個對象的引用指向的地址,hashcode 是一個本地方法,返回的是對象地址值。Ojbect類中有兩個方法equals、hashCode,這兩個方法都是用來比較兩個對象是否相等的。

為何重寫 equals方法的同時必須重寫 hashcode方法呢

可以這樣理解:重寫了 equals 方法,判斷對象相等的業務邏輯就變了,類的設計者不希望通過比較內存地址來比較兩個對象是否相等,而 hashcode 方法繼續按照地址去比較也沒有什么意義了,索性就跟著一起變吧。

還有一個原因來源于集合。下面慢慢說~

舉個例子:

在學校中,是通過學號來判斷是不是這個人的。

下面代碼中情景為學籍錄入,學號 123 被指定給學生 Tom,學號 456 被指定給學生 Jerry,學號 123 被失誤指定給 Lily。而在錄入學籍的過程中是不應該出現學號一樣的情況的。

根據情景需求是不能添加重復的對象,可以通過 HashSet 實現。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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 方法:

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

其實 HashSet 是通過 HashMap 實現的,由此我們追蹤到 HashMap 的 put 方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 方法,下面改造一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 的效率。

原文鏈接:http://www.cnblogs.com/xmsx/p/5705474.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 青青国产成人久久激情911 | 亚洲丰满女人ass硕大 | 欧美精品99久久久久久人 | 美女艹b | 免费被黄网站在观看 | 国产成人手机在线 | 欧美另类bbbxxxxx另类 | 成人性生交大片免费看软件 | 啊哈~嗯哼~用力cao我小说 | 非洲黑人又大粗gay 非洲黑人bbwbbwbbw | 国产第7页 | 国产清纯91天堂在线观看 | 日本高清中文字幕一区二区三区 | 粗了大了 整进去好爽视频 刺激一区仑乱 | 教室眠催白丝美女校花 | 天堂漫画破解版 | 厨房play黄瓜进去小说h | 亚洲国产成人久久精品hezyo | 国产一级片免费观看 | 欧美同性videos | 天天色国产 | 亚洲精品免费在线观看 | 欧美国产日韩1区俺去了 | 草草线在成年免费视频网站 | 91精品国产综合久久消防器材 | brazzersxxx欧美| 精品久久久久久久久久久 | 红杏网 | 日本高清视频在线免费观看 | 99热这里只有精品在线播放 | 欧美精品一区视频 | 亚洲国产美女精品久久久久 | 九九久久精品 | 国产精品国产国产aⅴ | gayrb免费漫画入口 | 国色天香社区在线 | 91精品国产综合久久消防器材 | 国产成人综合网亚洲欧美在线 | 任我鲁精品视频精品 | 久久精品国产清白在天天线 | 精品欧美一区二区精品久久 |