1、==
java中的數(shù)據(jù)類(lèi)型,可分為兩類(lèi):
1.基本數(shù)據(jù)類(lèi)型,也稱原始數(shù)據(jù)類(lèi)型
byte,short,char,int,long,float,double,boolean 他們之間的比較,應(yīng)用雙等號(hào)(==),比較的是他們的值。
2.引用類(lèi)型(類(lèi)、接口、數(shù)組)
當(dāng)他們用(==)進(jìn)行比較的時(shí)候,比較的是他們?cè)趦?nèi)存中的存放地址,所以,除非是同一個(gè)new出來(lái)的對(duì)象,他們的比較后的結(jié)果為true,否則比較后結(jié)果為false。
對(duì)象是放在堆中的,棧中存放的是對(duì)象的引用(地址)。由此可見(jiàn)'=='是對(duì)棧中的值進(jìn)行比較的。如果要比較堆中對(duì)象的內(nèi)容是否相同,那么就要重寫(xiě)equals方法了。
例:
public static void main(String[] args) {
int int1 = 12;
int int2 = 12;
Integer Integer1 = new Integer(12);
Integer Integer2 = new Integer(12);
Integer Integer3 = new Integer(127);
Integer a1 = 127;
Integer b1 = 127;
Integer a = 128;
Integer b = 128;
String s1 = "str";
String s2 = "str";
String str1 = new String("str");
String str2 = new String("str");
System.out.println("int1==int2:" + (int1 == int2));
System.out.println("int1==Integer1:" + (int1 == Integer1));
System.out.println("Integer1==Integer2:" + (Integer1 == Integer2));
System.out.println("Integer3==b1:" + (Integer3 == b1));
System.out.println("a1==b1:" + (a1 == b1));
System.out.println("a==b:" + (a == b));
System.out.println("s1==s2:" + (s1 == s2));
System.out.println("s1==str1:" + (s1 == str1));
System.out.println("str1==str2:" + (str1 == str2));
}
輸出結(jié)果:
int1==int2:true
int1==Integer1:true //Integer會(huì)自動(dòng)拆箱為int,所以為true
Integer1==Integer2:false//不同對(duì)象,在內(nèi)存存放地址不同,所以為false
Integer3==b1:false//Integer3指向new的對(duì)象地址,b1指向緩存中127地址,地址不同,所以為false
a1==b1:true
a==b:false
s1==s2:true
s1==str1:false
str1==str2:false
Integer b1 = 127;java在編譯的時(shí)候,被翻譯成-> Integer b1 = Integer.valueOf(127);
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
看一下源碼大家都會(huì)明白,對(duì)于-128到127之間的數(shù),會(huì)進(jìn)行緩存,Integer b1 = 127時(shí),會(huì)將127進(jìn)行緩存,下次再寫(xiě)Integer i6 = 127時(shí),就會(huì)直接從緩存中取,就不會(huì)new了。所以a1==b1:true a==b:false
2、equals
1、默認(rèn)情況(沒(méi)有覆蓋equals方法)下equals方法都是調(diào)用Object類(lèi)的equals方法,而Object的equals方法主要用于判斷對(duì)象的內(nèi)存地址引用是不是同一個(gè)地址(是不是同一個(gè)對(duì)象)。下面是Object類(lèi)中equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
定義的equals與==是等效的
2 、要是類(lèi)中覆蓋了equals方法,那么就要根據(jù)具體的代碼來(lái)確定equals方法的作用了,覆蓋后一般都是通過(guò)對(duì)象的內(nèi)容是否相等來(lái)判斷對(duì)象是否相等。下面是String類(lèi)對(duì)equals進(jìn)行了重寫(xiě):
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
即String中equals方法判斷相等的步驟是:
1.若A==B 即是同一個(gè)String對(duì)象 返回true
2.若對(duì)比對(duì)象是String類(lèi)型則繼續(xù),否則返回false
3.判斷A、B長(zhǎng)度是否一樣,不一樣的話返回false
4。逐個(gè)字符比較,若有不相等字符,返回false
這里對(duì)equals重新需要注意五點(diǎn):
1 自反性:對(duì)任意引用值X,x.equals(x)的返回值一定為true.
2 對(duì)稱性:對(duì)于任何引用值x,y,當(dāng)且僅當(dāng)y.equals(x)返回值為true時(shí),x.equals(y)的返回值一定為true;
3 傳遞性:如果x.equals(y)=true, y.equals(z)=true,則x.equals(z)=true
4 一致性:如果參與比較的對(duì)象沒(méi)任何改變,則對(duì)象比較的結(jié)果也不應(yīng)該有任何改變
5 非空性:任何非空的引用值X,x.equals(null)的返回值一定為false
實(shí)現(xiàn)高質(zhì)量equals方法的訣竅:
1.使用==符號(hào)檢查“參數(shù)是否為這個(gè)對(duì)象的引用”。如果是,則返回true。這只不過(guò)是一種性能優(yōu)化,如果比較操作有可能很昂貴,就值得這么做。
2.使用instanceof操作符檢查“參數(shù)是否為正確的類(lèi)型”。如果不是,則返回false。一般來(lái)說(shuō),所謂“正確的類(lèi)型”是指equals方法所在的那個(gè)類(lèi)。
3.把參數(shù)轉(zhuǎn)換成正確的類(lèi)型。因?yàn)檗D(zhuǎn)換之前進(jìn)行過(guò)instanceof測(cè)試,所以確保會(huì)成功。
4.對(duì)于該類(lèi)中的每個(gè)“關(guān)鍵”域,檢查參數(shù)中的域是否與該對(duì)象中對(duì)應(yīng)的域相匹配。如果這些測(cè)試全部成功,則返回true;否則返回false。
5.當(dāng)編寫(xiě)完成了equals方法之后,檢查“對(duì)稱性”、“傳遞性”、“一致性”。
3、hashCode
hashCode()方法返回的就是一個(gè)數(shù)值,從方法的名稱上就可以看出,其目的是生成一個(gè)hash碼。hash碼的主要用途就是在對(duì)對(duì)象進(jìn)行散列的時(shí)候作為key輸入,據(jù)此很容易推斷出,我們需要每個(gè)對(duì)象的hash碼盡可能不同,這樣才能保證散列的存取性能。事實(shí)上,Object類(lèi)提供的默認(rèn)實(shí)現(xiàn)確實(shí)保證每個(gè)對(duì)象的hash碼不同(在對(duì)象的內(nèi)存地址基礎(chǔ)上經(jīng)過(guò)特定算法返回一個(gè)hash碼)。Java采用了哈希表的原理。哈希(Hash)實(shí)際上是個(gè)人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。 哈希算法也稱為散列算法,是將數(shù)據(jù)依特定算法直接指定到一個(gè)地址上。初學(xué)者可以這樣理解,hashCode方法實(shí)際上返回的就是對(duì)象存儲(chǔ)的物理地址(實(shí)際可能并不是)。
散列函數(shù),散列算法,哈希函數(shù)。
是一種從任何一種數(shù)據(jù)中創(chuàng)建小的數(shù)字“指紋”的方法。
散列函數(shù)將任意長(zhǎng)度的二進(jìn)制值映射為較短的固定長(zhǎng)度的二進(jìn)制值,這個(gè)小的二進(jìn)制值稱為哈希值。
好的散列函數(shù)在輸入域中很少出現(xiàn)散列沖突。
=================================================================================
所有散列函數(shù)都有如下一個(gè)基本特性:
1:如果a=b,則h(a) = h(b)。
2:如果a!=b,則h(a)與h(b)可能得到相同的散列值。
Object 的hashCode方法:返回一個(gè)int類(lèi)型
public native int hashCode();
3.1 hashCode的作用
想要明白,必須要先知道Java中的集合?! ?br> 總的來(lái)說(shuō),Java中的集合(Collection)有兩類(lèi),一類(lèi)是List,再有一類(lèi)是Set。前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序,但元素不可重復(fù)。
那么這里就有一個(gè)比較嚴(yán)重的問(wèn)題了:要想保證元素不重復(fù),可兩個(gè)元素是否重復(fù)應(yīng)該依據(jù)什么來(lái)判斷呢?
這就是Object.equals方法了。但是,如果每增加一個(gè)元素就檢查一次,那么當(dāng)元素很多時(shí),后添加到集合中的元素比較的次數(shù)就非常多了。也就是說(shuō),如果集合中現(xiàn)在已經(jīng)有1000個(gè)元素,那么第1001個(gè)元素加入集合時(shí),它就要調(diào)用1000次equals方法。這顯然會(huì)大大降低效率。
于是,Java采用了哈希表的原理。
這樣一來(lái),當(dāng)集合要添加新的元素時(shí),
先調(diào)用這個(gè)元素的hashCode方法,就一下子能定位到它應(yīng)該放置的物理位置上。
如果這個(gè)位置上沒(méi)有元素,它就可以直接存儲(chǔ)在這個(gè)位置上,不用再進(jìn)行任何比較了;
如果這個(gè)位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進(jìn)行比較,相同的話就不存,不相同就散列其它的地址。所以這里存在一個(gè)沖突解決的問(wèn)題。這樣一來(lái)實(shí)際調(diào)用equals方法的次數(shù)就大大降低了,幾乎只需要一兩次。
4、eqauls方法和hashCode方法關(guān)系
Java對(duì)于eqauls方法和hashCode方法是這樣規(guī)定的:
(1)同一對(duì)象上多次調(diào)用hashCode()方法,總是返回相同的整型值。
(2)如果a.equals(b),則一定有a.hashCode() 一定等于 b.hashCode()。
(3)如果!a.equals(b),則a.hashCode() 不一定等于 b.hashCode()。此時(shí)如果a.hashCode() 總是不等于 b.hashCode(),會(huì)提高h(yuǎn)ashtables的性能。
(4)a.hashCode()==b.hashCode() 則 a.equals(b)可真可假
(5)a.hashCode()!= b.hashCode() 則 a.equals(b)為假。
上面結(jié)論簡(jiǎn)記:
1、如果兩個(gè)對(duì)象equals,Java運(yùn)行時(shí)環(huán)境會(huì)認(rèn)為他們的hashcode一定相等。
2、如果兩個(gè)對(duì)象不equals,他們的hashcode有可能相等。
3、如果兩個(gè)對(duì)象hashcode相等,他們不一定equals。
4、如果兩個(gè)對(duì)象hashcode不相等,他們一定不equals。
關(guān)于這兩個(gè)方法的重要規(guī)范:
規(guī)范1:若重寫(xiě)equals(Object obj)方法,有必要重寫(xiě)hashcode()方法,確保通過(guò)equals(Object obj)方法判斷結(jié)果為true的兩個(gè)對(duì)象具備相等的hashcode()返回值。說(shuō)得簡(jiǎn)單點(diǎn)就是:“如果兩個(gè)對(duì)象相同,那么他們的hashcode應(yīng)該相等”。不過(guò)請(qǐng)注意:這個(gè)只是規(guī)范,如果你非要寫(xiě)一個(gè)類(lèi)讓equals(Object obj)返回true而hashcode()返回兩個(gè)不相等的值,編譯和運(yùn)行都是不會(huì)報(bào)錯(cuò)的。不過(guò)這樣違反了Java規(guī)范,程序也就埋下了BUG。
規(guī)范2:如果equals(Object obj)返回false,即兩個(gè)對(duì)象“不相同”,并不要求對(duì)這兩個(gè)對(duì)象調(diào)用hashcode()方法得到兩個(gè)不相同的數(shù)。說(shuō)的簡(jiǎn)單點(diǎn)就是:“如果兩個(gè)對(duì)象不相同,他們的hashcode可能相同”。
5、為什么覆蓋equals時(shí)總要覆蓋hashCode
一個(gè)很常見(jiàn)的錯(cuò)誤根源在于沒(méi)有覆蓋hashCode方法。在每個(gè)覆蓋了equals方法的類(lèi)中,也必須覆蓋hashCode方法。如果不這樣做的話,就會(huì)違反Object.hashCode的通用約定,從而導(dǎo)致該類(lèi)無(wú)法結(jié)合所有基于散列的集合一起正常運(yùn)作,這樣的集合包括HashMap、HashSet和Hashtable。
1.在應(yīng)用程序的執(zhí)行期間,只要對(duì)象的equals方法的比較操作所用到的信息沒(méi)有被修改,那么對(duì)這同一個(gè)對(duì)象調(diào)用多次,hashCode方法都必須始終如一地返回同一個(gè)整數(shù)。在同一個(gè)應(yīng)用程序的多次執(zhí)行過(guò)程中,每次執(zhí)行所返回的整數(shù)可以不一致。
2.如果兩個(gè)對(duì)象根據(jù)equals()方法比較是相等的,那么調(diào)用這兩個(gè)對(duì)象中任意一個(gè)對(duì)象的hashCode方法都必須產(chǎn)生同樣的整數(shù)結(jié)果。
3.如果兩個(gè)對(duì)象根據(jù)equals()方法比較是不相等的,那么調(diào)用這兩個(gè)對(duì)象中任意一個(gè)對(duì)象的hashCode方法,則不一定要產(chǎn)生相同的整數(shù)結(jié)果。但是程序員應(yīng)該知道,給不相等的對(duì)象產(chǎn)生截然不同的整數(shù)結(jié)果,有可能提高散列表的性能。
6、總結(jié):
1、equals方法用于比較對(duì)象的內(nèi)容是否相等(覆蓋以后)
2、hashcode方法只有在集合中用到
3、當(dāng)覆蓋了equals方法時(shí),比較對(duì)象是否相等將通過(guò)覆蓋后的equals方法進(jìn)行比較(判斷對(duì)象的內(nèi)容是否相等)。
4、將對(duì)象放入到集合中時(shí),首先判斷要放入對(duì)象的hashcode值與集合中的任意一個(gè)元素的hashcode值是否相等,如果不相等直接將該對(duì)象放入集合中。如果hashcode值相等,然后再通過(guò)equals方法判斷要放入對(duì)象與集合中的任意一個(gè)對(duì)象是否相等,如果equals判斷不相等,直接將該元素放入到集合中,否則不放入。