Java ==,equals()與hashcode()的使用
==,equals()與hashcode()
"=="
在講解之前,我們是怎么接觸到==的,我們?cè)谧鞅容^時(shí)經(jīng)常用到 ==, 這個(gè)符號(hào)常用作基本類的數(shù)據(jù)。
==運(yùn)算符通常用于比較基本數(shù)據(jù)類型(如int、long、double等)和枚舉類型的值是否相等,而對(duì)于對(duì)象類型,==運(yùn)算符用于比較它們的引用是否相等,即它們是否指向同一個(gè)對(duì)象。
思考一個(gè)問題,為什么我們一般不使用==來比較對(duì)象,如果兩個(gè)對(duì)象的引用相同,即它們指向同一個(gè)對(duì)象,那么使用==運(yùn)算符比較它們的引用會(huì)返回true,因?yàn)樗鼈兇_實(shí)指向同一個(gè)對(duì)象。在這種情況下,兩個(gè)對(duì)象的內(nèi)容自然是相同的,因?yàn)樗鼈兪峭粋€(gè)對(duì)象。
那為啥我們不直接使用==去比較對(duì)象, 而要麻煩的使用其它的辦法,
因?yàn)? 我們比較對(duì)象不是想看它的引用是否相同,而是里面的屬性是否相同
在這種情況下,使用==運(yùn)算符比較兩個(gè)對(duì)象的引用并不能判斷它們的內(nèi)容是否相同,因?yàn)閮蓚€(gè)不同的對(duì)象可能具有相同的屬性值,但它們的引用不同。因此,在比較對(duì)象是否相同時(shí),應(yīng)該根據(jù)對(duì)象的語義來實(shí)現(xiàn)equals()方法,而不是使用==運(yùn)算符。
public class Main {
public static void main(String[] args) {
int a = 5;
int b = 5;
int c = 6;
System.out.println(a==b); // true
System.out.println(a==c); // false
Person p1 = new Person("hhh", 15);
Person p2 = new Person("hhh", 15);
// 屬性相同,但引用不同。按照常理應(yīng)該被認(rèn)作是相同的。實(shí)際卻是不同的
System.out.println(p1 == p2); // false
}
}
class Person{
String name;
Integer age;
Person(String name, Integer age){
this.age = age;
this.name = name;
}
}equals()方法
equals()方法是Object類中的方法,它僅僅是地址的比較,因此只能比較兩個(gè)類是否是同一個(gè)類,而不能比較屬性是否相同,因此,我們比較對(duì)象一般都會(huì)重寫equals()方法
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// getters and setters
@Override
public boolean equals(Object obj) {
return (this == obj);
}
}如果一個(gè)類的屬性包含基本數(shù)據(jù)類型和引用數(shù)據(jù)類型,那么這些屬性值相同,但它們的引用地址也不一定相同。此時(shí),必須重寫equals()方法,根據(jù)對(duì)象的語義來進(jìn)行比較,才能得到正確的結(jié)果。
列表中的使用
在Java中,列表類(如List)通常用來存儲(chǔ)一組對(duì)象,當(dāng)我們需要在列表中查找某個(gè)對(duì)象時(shí),通常需要調(diào)用列表的contains()方法或indexOf()方法。這些方法會(huì)使用對(duì)象的equals()方法來比較對(duì)象是否相等。
它是通過比較對(duì)象的屬性值從而去列表中查找。
public class Main {
public static void main(String[] args) {
List<String> list = List.of("A", "B", "C");
// 下面的例子中即使對(duì)象的地址不同,但是屬性值相同,因此在列表中能查到
System.out.println(list.contains(new String("C"))); // true
System.out.println(list.indexOf(new String("C"))); // 2
}
}在hash結(jié)構(gòu)的表中使用
map是一種鍵值對(duì)的映射表,map使用一個(gè)很大的數(shù)組來存儲(chǔ)value,然后通過key的hash值來查找value的索引
map其實(shí)可以看作一個(gè)數(shù)組,key的hashcode()值可以作為數(shù)組的索引,然后根據(jù)索引去查找value。
那究竟在什么地方使用equals()呢?
當(dāng)我們輸入key的時(shí)候,map需要將傳入的key與當(dāng)前數(shù)組中存入的key相比較。
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put(new String("sss"),"hhh");
map.put(new String("sss"), "aaa");
// map里只有一個(gè)數(shù)據(jù),因?yàn)镾tring重寫了equals方法,所以key屬性值一樣的會(huì)被覆蓋
System.out.println(map.size());
HashMap<Person, String> map1 = new HashMap<>();
map1.put(new Person("xiao",15), "ok");
map1.put(new Person("xiao",15), "hao");
// map1里有兩個(gè)數(shù)據(jù),明明key是一樣的,卻沒有覆蓋
System.out.println(map1.size());
}
}
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}- 添加重復(fù)的鍵值對(duì):由于
Map將不同的對(duì)象視為不同的鍵,如果我們使用相同類型的不同對(duì)象作為鍵,即使它們的屬性值相同,Map也會(huì)將它們視為不同的鍵,從而導(dǎo)致添加重復(fù)的鍵值對(duì)。 - 無法查找鍵值對(duì):由于
Map將不同的對(duì)象視為不同的鍵,如果我們使用一個(gè)對(duì)象作為鍵添加了一個(gè)鍵值對(duì),而后又使用另一個(gè)對(duì)象作為鍵查找該鍵值對(duì),那么Map會(huì)認(rèn)為這兩個(gè)對(duì)象是不同的鍵,因此無法找到對(duì)應(yīng)的鍵值對(duì)。
因此,我們需要正確重寫equals()方法。
hashcode()方法
HashMap為什么能通過key直接計(jì)算出value存儲(chǔ)的索引。相同的key對(duì)象(使用equals()判斷時(shí)返回true)必須要計(jì)算出相同的索引,否則,相同的key每次取出的value就不一定對(duì)。
map是通過hashcode()去尋找value數(shù)組索引的,所以還需要為對(duì)象覆寫hashcode()方法。
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put(new String("sss"),"hhh");
map.put(new String("sss"), "aaa");
// map里只有一個(gè)數(shù)據(jù),因?yàn)镾tring重寫了equals方法和hashcode方法,所以key屬性值一樣的會(huì)被覆蓋
System.out.println(map.size()); // 1
HashMap<Person, String> map1 = new HashMap<>();
Person p1 = new Person("xiao", 15);
map1.put(p1, "ok");
Person p2 = new Person("xiao", 15);
map1.put(p2, "hao");
// map現(xiàn)在也只有一個(gè)數(shù)據(jù)了,因?yàn)閮蓚€(gè)相同屬性的對(duì)象被視作同一個(gè)
System.out.println(map1.size()); // 1
}
}
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
// 覆寫equals方法
@Override
public boolean equals(Object o) {
if (o instanceof Person p) {
return this.name.equals(p.name) && this.age == p.age;
}
return false;
}
// 覆寫hashcode方法
@Override
public int hashCode() {
int h = 0;
h = 31 * h + name.hashCode();
h = 31 * h + age;
return h;
}
}map的put方法里是用到了hash值的,因此不正確重寫hash值而正確重寫equals方法是沒用的。
小節(jié)
比較基本數(shù)據(jù)類型或?qū)ο蟮刂酚?code>==, 比較對(duì)象地址使用Object的equals方法, 比較對(duì)象屬性值使用覆寫之后的equals().
對(duì)于list集合數(shù)據(jù)類型,本質(zhì)是數(shù)組,當(dāng)存儲(chǔ)對(duì)象數(shù)據(jù)類型的值時(shí),重寫equals方法才能使用contains()等需要使用equals進(jìn)行比較的方法。
對(duì)于hash table類型的數(shù)據(jù),必須覆寫equals與hashcode方法,才能正常工作。
到此這篇關(guān)于Java ==,equals()與hashcode()的使用的文章就介紹到這了,更多相關(guān)Java ==,equals()與hashcode()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java隨機(jī)數(shù)生成具體實(shí)現(xiàn)代碼
這篇文章主要為大家分享了java隨機(jī)數(shù)生成具體實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04
tio-boot?jfinal-plugins框架整合redis示例詳解
這篇文章主要為大家介紹了tio-boot?jfinal-plugins框架整合redis示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
SpringMVC參數(shù)傳遞之基本數(shù)據(jù)類型和復(fù)雜對(duì)象說明
這篇文章主要介紹了SpringMVC參數(shù)傳遞之基本數(shù)據(jù)類型和復(fù)雜對(duì)象說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
基于SpringCloud手寫一個(gè)簡易版Sentinel
SpringCloud Alibaba Sentinel是當(dāng)前最為流行一種熔斷降級(jí)框架,簡單易用的方式可以快速幫助我們實(shí)現(xiàn)服務(wù)的限流和降級(jí),保證服務(wù)的穩(wěn)定性。2021-05-05
Java實(shí)現(xiàn)導(dǎo)出word表格的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言導(dǎo)出word表格功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考一下2022-12-12
SpringBoot項(xiàng)目nohup啟動(dòng)運(yùn)行日志過大的解決方案
這篇文章主要介紹了SpringBoot項(xiàng)目nohup啟動(dòng)運(yùn)行日志過大的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05

