1、==
java中的數(shù)據(jù)類型亭病,可分為兩類:
- 基本數(shù)據(jù)類型,也稱原始數(shù)據(jù)類型
byte,short,char,int,long,float,double,boolean 他們之間的比較嘶居,應(yīng)用雙等號(hào)(==),比較的是他們的值罪帖。
- 引用類型(類、接口邮屁、數(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)容是否相同痹仙,那么就要重寫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)行緩存傻粘,下次再寫Integer i6 = 127時(shí)每窖,就會(huì)直接從緩存中取,就不會(huì)new了弦悉。所以a1==b1:true a==b:false
2窒典、equals
- 默認(rèn)情況(沒(méi)有覆蓋equals方法)下equals方法都是調(diào)用Object類的equals方法,而Object的equals方法主要用于判斷對(duì)象的內(nèi)存地址引用是不是同一個(gè)地址(是不是同一個(gè)對(duì)象)稽莉。下面是Object類中equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
定義的equals與==是等效的
- 要是類中覆蓋了equals方法瀑志,那么就要根據(jù)具體的代碼來(lái)確定equals方法的作用了,覆蓋后一般都是通過(guò)對(duì)象的內(nèi)容是否相等來(lái)判斷對(duì)象是否相等污秆。下面是String類對(duì)equals進(jìn)行了重寫:
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類型則繼續(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ù)是否為正確的類型”致讥。如果不是,則返回false器赞。一般來(lái)說(shuō)垢袱,所謂“正確的類型”是指equals方法所在的那個(gè)類。
3.把參數(shù)轉(zhuǎn)換成正確的類型港柜。因?yàn)檗D(zhuǎn)換之前進(jìn)行過(guò)instanceof測(cè)試请契,所以確保會(huì)成功咳榜。
4.對(duì)于該類中的每個(gè)“關(guān)鍵”域,檢查參數(shù)中的域是否與該對(duì)象中對(duì)應(yīng)的域相匹配爽锥。如果這些測(cè)試全部成功涌韩,則返回true;否則返回false。
5.當(dāng)編寫完成了equals方法之后氯夷,檢查“對(duì)稱性”臣樱、“傳遞性”、“一致性”腮考。
3雇毫、hashCode
hashCode()方法返回的就是一個(gè)數(shù)值,從方法的名稱上就可以看出踩蔚,其目的是生成一個(gè)hash碼棚放。hash碼的主要用途就是在對(duì)對(duì)象進(jìn)行散列的時(shí)候作為key輸入,據(jù)此很容易推斷出馅闽,我們需要每個(gè)對(duì)象的hash碼盡可能不同飘蚯,這樣才能保證散列的存取性能。事實(shí)上福也,Object類提供的默認(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類型
public native int hashCode();
hashCode的作用
想要明白,必須要先知道Java中的集合贴硫〈幻浚
總的來(lái)說(shuō)伊者,Java中的集合(Collection)有兩類,一類是List间护,再有一類是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:若重寫equals(Object obj)方法,有必要重寫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ī)范瘦穆,如果你非要寫一個(gè)類讓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方法的類中,也必須覆蓋hashCode方法淮椰。如果不這樣做的話五慈,就會(huì)違反Object.hashCode的通用約定纳寂,從而導(dǎo)致該類無(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判斷不相等朋腋,直接將該元素放入到集合中,否則不放入膜楷。