一、equals()和==
1.以下是Object類中equals()的源碼
public boolean equals(Object obj) {
return (this == obj);
}
2.以下是Integer類中equals()的源碼
public boolean equals(Object obj) {
if (obj instanceof Integer) {
//返回當(dāng)前對(duì)象的值與參數(shù)對(duì)象的值的比較
return value == ((Integer)obj).intValue();
}
return false;
}
3.以下是String類中equals()的源碼
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
//判斷參數(shù)是否為String類對(duì)象,不是直接返回false
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
//比較每個(gè)字符
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;
}
由以上源碼我們可以得出:
① equal和==最根本的區(qū)別在于equal是一個(gè)方法,而==是一個(gè)運(yùn)算符。
② 基本數(shù)據(jù)類型只能用==來進(jìn)行比較。在object類中equal()方法就是用==實(shí)現(xiàn)的,但是對(duì)于,Math、Integer、Double等這些封裝類在使用equals()方法時(shí),已經(jīng)覆蓋了object類的equals()方法,他們比較的是內(nèi)容(值)。
③ 在String類中equals()也被重寫了,因此字符串比較返回true有兩種情況(==:地址內(nèi)容都一致,equals():內(nèi)容一致)
題外話:下面代碼的運(yùn)行結(jié)果是什么?解釋一下為什么會(huì)有這些差異。
String s1 = "hello";
String s2 = s1 + ",world";
String s3 = "hello" + ",world";
String s4 = "hello,world";
String s5 = new String("hello,world");
System.out.println(s2.equals(s4)); // true
System.out.println(s2==s4); // false
System.out.println(s3==s4); // true
System.out.println(s4==s5); // false
一般情況下,創(chuàng)建字符串對(duì)象有兩種方式,一種是字面值創(chuàng)建,一種是通過 new 得方式創(chuàng)建,這兩者是不一樣的。
如果是字面值創(chuàng)建的方式,如 String s4="hello,world",JVM會(huì)先去字符串常量池中尋找有沒有“hello,world”這個(gè)字符串,若有,則將其地址給 s4;若沒有,則先在常量池里創(chuàng)建“hello,world”,然后再把地址給s4;而通過 new 的方式創(chuàng)建對(duì)象,則是在堆中創(chuàng)建“hello,world”對(duì)象,s5 指向這個(gè)對(duì)象。還是看圖吧,比較直觀:
字符串的拼接
String 類被 final 修飾,因此字符串不能修改,當(dāng)兩個(gè)字符串相加時(shí),是先生成 StringBuilder 對(duì)象,然后通過 append() 的方式將兩個(gè)字符串拼接,再調(diào)用 toString() 方法生成新的字符串對(duì)象。所以只考慮 s1 和 s2,他們?cè)?JVM 中是這樣的:
但是像 String s3 = "hello" + ",world" 這樣直接兩個(gè)字面值相加的,java文件在編譯期間就已經(jīng)將這條語句做了優(yōu)化,將其直接變成 "hello,world",等到運(yùn)行的時(shí)候就查找字符串常量池,因此 s3 == s4 返回的結(jié)果就為 true。
String、StringBuilder、StringBuffer
String 是常量,且每次需要在原來字符串基礎(chǔ)上擴(kuò)展都需要新建對(duì)象,導(dǎo)致速度會(huì)稍微慢一點(diǎn),而且占內(nèi)存,因此對(duì)于需要經(jīng)常擴(kuò)展的字符串,可以使用 StringBuilder 和 StringBuffer。但是 StringBuilder 和 StringBuffer 又有區(qū)別,即使兩者很像,在速度上 StringBuilder 會(huì)快一些,因?yàn)?StringBuffer 的操作加了 synchronized ,即加了鎖,使得操作相對(duì)比較慢,就舉 append() 為例子,StringBuffer 中此方法的源碼如下:
所以,一般在單線程的情況下,可以選擇使用 StringBuilder;在多線程的情況下可以選擇 StringBuffer .
總結(jié)
回到一開始的程序,我們可以大致地畫出在 JVM 中的存儲(chǔ):二、hashcode()
hashCode()方法給對(duì)象返回一個(gè)hash code值。
性質(zhì):
- 在一個(gè)Java應(yīng)用的執(zhí)行期間,如果一個(gè)對(duì)象提供給equals做比較的信息沒有被修改的話,該對(duì)象多次調(diào)用hashCode()方法,該方法必須始終如一返回同一個(gè)integer。
- 如果兩個(gè)對(duì)象根據(jù)equals(Object)方法是相等的,那么調(diào)用二者各自的hashCode()方法必須產(chǎn)生同一個(gè)integer結(jié)果。
- 并不要求根據(jù)equals(java.lang.Object)方法不相等的兩個(gè)對(duì)象,調(diào)用二者各自的hashCode()方法必須產(chǎn)生不同的integer結(jié)果。[1]
。
1.以下是Object類中hashcode()的源碼,說明是一個(gè)本地方法,它的實(shí)現(xiàn)是根據(jù)本地機(jī)器相關(guān)的。大量的實(shí)踐表明,由Object類定義的hashCode()方法對(duì)于不同的對(duì)象返回不同的integer
public native int hashCode();
2.以下是String類中hashcode()的源碼,可以看出字符串的內(nèi)容相同hashcode也相同。
public int hashCode() {
int h = hash; //hash默認(rèn)值是0
//字符串的內(nèi)容相同hashcode也相同
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
3.以下是AbstractSet類中hashcode()和equals()的源碼
在set集合中,jvm就是根據(jù)hashcode(),和equals()這兩個(gè)方法來判斷元素是否重復(fù),如果要重寫,建議兩個(gè)方法一起重寫,否則可能會(huì)出現(xiàn)安全性問題。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
4.以下是AbstractList類中hashcode()和equals()的源碼
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
參考:
https://mp.weixin.qq.com/s/2rTxX3Co-2bDR16wZE-WwQ
https://www.cnblogs.com/Qian123/p/5703507.html
-
兩個(gè)對(duì)象equals()結(jié)果相同則兩者h(yuǎn)ashcode()也相同;而兩者h(yuǎn)ashcode()相同,equals()結(jié)果不一定相同;因此,在Set中判斷元素是否重復(fù)的其中一個(gè)重要原則就是先判斷hashcode,若相同再判斷equals()是否為true。 ?