淺談String類型等值比較引起的“==”、“equals()”和“hashCode”思考
關(guān)于String類型的等值比較和內(nèi)容比較,是學(xué)習(xí)java甚至任何編程語(yǔ)言所共同的常見(jiàn)問(wèn)題,理解String類型的等值比較和內(nèi)容比較也是面試經(jīng)常問(wèn)到的問(wèn)題。
String類型的等值比較和內(nèi)容比較
字符串等值比較
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
首先,如上述String的源碼可以知道的是,java中等值比較,就是“==”比較,比較的是地址。字符串本質(zhì)上是final修飾的字符數(shù)組,也就是說(shuō),當(dāng)創(chuàng)建字符串對(duì)象時(shí),字符串的引用是常量,但它每一個(gè)對(duì)象的值可以改變。
字符串內(nèi)容比較
如上述String的源碼可以知道的是,字符串的內(nèi)容比較就是所謂的字符串的equals()方法,比較的是兩個(gè)字符串對(duì)象儲(chǔ)存的值,也就是內(nèi)容是否相等,所謂內(nèi)容相同,就是字符串每一個(gè)位置的字符相同。這里值得注意的是String重寫(xiě)了Object的equals()方法(Object的==與equals()是一樣的)
地址分配圖示

很明顯str1和str2的地址相同,他們與str3的地址不相同,但是str3通過(guò)方法intern(),可以強(qiáng)制入池,和str1和str2的地址相同。
結(jié)論
字符串:默認(rèn)為常量------------進(jìn)常量池
String val = “xxx”;---------------------------默認(rèn)入池
String val = new String(“xxx”);--------------------------默認(rèn)入堆,但可以通過(guò)intern()強(qiáng)制入池(堆里的對(duì)象還在)
上面只是簡(jiǎn)單的介紹string類的對(duì)象等值比較,下面我們來(lái)深刻的了解下java 對(duì)象的等值判斷。
對(duì)象相等判斷
== 和 equals 的區(qū)別是什么
== : 它的作用是判斷兩個(gè)對(duì)象的地址是不是相等。即,判斷兩個(gè)對(duì)象是不是同一個(gè)對(duì)象。(基本數(shù)據(jù)類型 == 比較的是值,引用數(shù)據(jù)類型 == 比較的是內(nèi)存地址)
equals() : 它的作用也是判斷兩個(gè)對(duì)象是否相等。但它一般有兩種使用情況:
情況1:類沒(méi)有覆蓋 equals() 方法。則通過(guò) equals() 比較該類的兩個(gè)對(duì)象時(shí),等價(jià)于通過(guò)“==”比較這兩個(gè)對(duì)象。
情況2:類覆蓋了 equals() 方法。一般,我們都覆蓋 equals() 方法來(lái)兩個(gè)對(duì)象的內(nèi)容相等;若它們的內(nèi)容相等,則返回 true (即,認(rèn)為這兩個(gè)對(duì)象相等)。
舉個(gè)例子:
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 為一個(gè)引用
String b = new String("ab"); // b為另一個(gè)引用,對(duì)象的內(nèi)容一樣
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 從常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一對(duì)象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
說(shuō)明:
String中的equals方法是被重寫(xiě)過(guò)的,因?yàn)閛bject的equals方法是比較的對(duì)象的內(nèi)存地址,而String的equals方法比較的是對(duì)象的值。
當(dāng)創(chuàng)建String類型的對(duì)象時(shí),虛擬機(jī)會(huì)在常量池中查找有沒(méi)有已經(jīng)存在的值和要?jiǎng)?chuàng)建的值相同的對(duì)象,如果有就把它賦給當(dāng)前引用。如果沒(méi)有就在常量池中重新創(chuàng)建一個(gè)String對(duì)象。
hashCode 與 equals (重要)
HashSet如何檢查重復(fù)
兩個(gè)對(duì)象的 hashCode() 相同,則 equals() 也一定為 true,對(duì)嗎?
hashCode和equals方法的關(guān)系
面試官可能會(huì)問(wèn)你:“你重寫(xiě)過(guò) hashcode 和 equals 么,為什么重寫(xiě)equals時(shí)必須重寫(xiě)hashCode方法?”
hashCode()介紹
hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實(shí)際上是返回一個(gè)int整數(shù)。這個(gè)哈希碼的作用是確定該對(duì)象在哈希表中的索引位置。hashCode() 定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode()函數(shù)。
散列表存儲(chǔ)的是鍵值對(duì)(key-value),它的特點(diǎn)是:能根據(jù)“鍵”快速的檢索出對(duì)應(yīng)的“值”。這其中就利用到了散列碼?。梢钥焖僬业剿枰膶?duì)象)
為什么要有 hashCode
我們以“HashSet 如何檢查重復(fù)”為例子來(lái)說(shuō)明為什么要有 hashCode:
當(dāng)你把對(duì)象加入 HashSet 時(shí),HashSet 會(huì)先計(jì)算對(duì)象的 hashcode 值來(lái)判斷對(duì)象加入的位置,同時(shí)也會(huì)與其他已經(jīng)加入的對(duì)象的 hashcode 值作比較,如果沒(méi)有相符的hashcode,HashSet會(huì)假設(shè)對(duì)象沒(méi)有重復(fù)出現(xiàn)。但是如果發(fā)現(xiàn)有相同 hashcode 值的對(duì)象,這時(shí)會(huì)調(diào)用 equals()方法來(lái)檢查 hashcode 相等的對(duì)象是否真的相同。如果兩者相同,HashSet 就不會(huì)讓其加入操作成功。如果不同的話,就會(huì)重新散列到其他位置。這樣我們就大大減少了 equals 的次數(shù),相應(yīng)就大大提高了執(zhí)行速度。
hashCode()與equals()的相關(guān)規(guī)定
如果兩個(gè)對(duì)象相等,則hashcode一定也是相同的
兩個(gè)對(duì)象相等,對(duì)兩個(gè)對(duì)象分別調(diào)用equals方法都返回true
兩個(gè)對(duì)象有相同的hashcode值,它們也不一定是相等的
因此,equals 方法被覆蓋過(guò),則 hashCode 方法也必須被覆蓋
hashCode() 的默認(rèn)行為是對(duì)堆上的對(duì)象產(chǎn)生獨(dú)特值。如果沒(méi)有重寫(xiě) hashCode(),則該 class 的兩個(gè)對(duì)象無(wú)論如何都不會(huì)相等(即使這兩個(gè)對(duì)象指向相同的數(shù)據(jù))
對(duì)象的相等與指向他們的引用相等,兩者有什么不同?
對(duì)象的相等 比的是內(nèi)存中存放的內(nèi)容是否相等而 引用相等 比較的是他們指向的內(nèi)存地址是否相等。
以上這篇淺談String類型等值比較引起的“==”、“equals()”和“hashCode”思考就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁(yè)示例
本文介紹了SpringBoot+MyBatis-Plus實(shí)現(xiàn)分頁(yè)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
教你怎么解決IDEA中“\t“空格顯示不正確的問(wèn)題
在之前進(jìn)行先來(lái)先服務(wù)(FCFS)調(diào)度算法的模擬過(guò)程中,使用IDEA進(jìn)行開(kāi)發(fā)時(shí)遇到了"\t"無(wú)法補(bǔ)全當(dāng)前字符串長(zhǎng)度到8的整數(shù)倍的問(wèn)題.百度一圈發(fā)現(xiàn)也有很多程序員遇到了這樣的問(wèn)題,但是沒(méi)有解決方法,于是有了這篇文章,需要的朋友可以參考下2021-05-05
Java 在Word中創(chuàng)建郵件合并模板并合并文本和圖片的操作方法
通過(guò)Java程序展示如何來(lái)實(shí)現(xiàn)創(chuàng)建模板,并通過(guò)郵件合并功能來(lái)合并文本數(shù)據(jù)和圖片數(shù)據(jù)的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07
SpringBoot中項(xiàng)目如何讀取外置logback配置文件
這篇文章主要介紹了SpringBoot中項(xiàng)目如何讀取外置logback配置文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
idea創(chuàng)建Springboot多模塊項(xiàng)目(聚合項(xiàng)目)
文章詳細(xì)介紹了如何在idea創(chuàng)建Springboot多模塊項(xiàng)目(聚合項(xiàng)目),包括創(chuàng)建父工程和子工程、編輯pom.xml文件、編寫(xiě)代碼和測(cè)試,還介紹了如何處理Maven視圖中的層級(jí)關(guān)系,并展示了如何同時(shí)啟動(dòng)多個(gè)子項(xiàng)目2024-11-11
SpringBoot + JPA @ManyToMany的操作要點(diǎn)說(shuō)明
這篇文章主要介紹了SpringBoot + JPA @ManyToMany的操作要點(diǎn)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
為什么ConcurrentHashMap的key value不能為null,map可以?
這篇文章主要介紹了為什么ConcurrentHashMap的key value不能為null,map可以呢?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
Java中通過(guò)三級(jí)緩存解決Spring循環(huán)依賴詳解
這篇文章主要介紹了Java中通過(guò)三級(jí)緩存解決Spring循環(huán)依賴詳解,當(dāng)出現(xiàn)兩個(gè)或多個(gè) Bean 在初始化時(shí)相互依賴的情況時(shí),Spring Boot 會(huì)將其中一個(gè) Bean 提前暴露出來(lái),以便其他 Bean 能夠在初始化時(shí)正確地引用它,這一策略能有效避免循環(huán)依賴導(dǎo)致的問(wèn)題,需要的朋友可以參考下2023-09-09

