float foo=42e1为什么错_为什么重写了equals()也要重写hashCode()
小Hub領(lǐng)讀:
雖然是很基礎(chǔ)的一篇文章,但是對(duì)于equals、hashcode兩個(gè)方法,相信很多人都與其中的規(guī)則不熟悉,來(lái)跟著小Hub花個(gè)8分鐘回顧一下!
作者:不學(xué)無(wú)數(shù)的程序員
https://my.oschina.net/u/4030990/blog/3134199
在?EffectiveJava中第九條規(guī)定在覆蓋?equals()方法時(shí)總要覆蓋?hashCode()方法。這是為什么呢?接下來(lái)我們就介紹一下這兩個(gè)方法。
Java中的?equals()方法和?hashCode()方法都是在?Object類中的方法,而在Java中所有的類都是?Obejct類的子類,所以Java中所有的方法都會(huì)有這兩個(gè)方法的默認(rèn)實(shí)現(xiàn)。
equals方法
Object類中的?equals()方法定義如下
public boolean equals(Object obj) {
return (this == obj);
}
我們發(fā)現(xiàn)在?equals()方法中就關(guān)鍵的?==,那么?==在Java中有什么含義呢,我們都知道在Java中分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。那么?==在這兩個(gè)類型中作用是不一樣的。
基本數(shù)據(jù)類型:比較的是?==兩邊值是否相等
引用數(shù)據(jù)類型:比較的是?==兩邊內(nèi)存地址是否相等
基本數(shù)據(jù)類型包括:?byte,?short,?char,?int,?long,?float,?double,?boolean
而通過(guò)Java文檔中的?equals()方法描述,所有要實(shí)現(xiàn)自己的?equals()方法都要遵守下面幾個(gè)規(guī)則
自反性:對(duì)于任何對(duì)象x,?x.equals(x)應(yīng)該返回?true
對(duì)稱性:對(duì)于任何兩個(gè)對(duì)象x和y,如果?x.equals(y)返回?true,那么?y.equals(x)也應(yīng)該返回?true
傳遞性:對(duì)于多個(gè)對(duì)象x,y,z,如果?x.equals(y)返回?true,?y.equals(z)返回?true,那么?y.equals(z)也應(yīng)該返回?true
一致性:對(duì)于兩個(gè)非空對(duì)象x,y,在沒有修改此對(duì)象的前提下,多次調(diào)用返回的結(jié)果應(yīng)該相同
對(duì)于任何非空的對(duì)象x,?x.equals(null)都應(yīng)該返回?false
hashCode方法
Object中的?hashCode()方法是一個(gè)本地方法,返回一個(gè)?int類型的哈希值。
public native int hashCode();
在?hashCode()方法中也有一些規(guī)約
如果對(duì)象在使用?equals方法中進(jìn)行比較的參數(shù)沒有修改,那么多次調(diào)用一個(gè)對(duì)象的?hashCode()方法返回的哈希值應(yīng)該是相同的。
如果兩個(gè)對(duì)象通過(guò)?equals方法比較是相等的,那么要求這兩個(gè)對(duì)象的?hashCode方法返回的值也應(yīng)該是相等的。
如果兩個(gè)對(duì)象通過(guò)?equals方法比較是不同的,那么也不要求這兩個(gè)對(duì)象的?hashCode方法返回的值是相同的。但是我們應(yīng)該知道對(duì)于不同對(duì)象產(chǎn)生不同的哈希值對(duì)于哈希表(HashMap等等)能夠提高性能。
equals方法和hashCode方法會(huì)在哪用到
這兩個(gè)方法經(jīng)常出現(xiàn)在Java中的哪個(gè)類里面呢?如果看過(guò)?HashMap源碼的應(yīng)該了解這兩個(gè)方法經(jīng)常出現(xiàn)在?HashMap中。網(wǎng)上介紹?HashMap類的文章有很多了,這里就簡(jiǎn)單介紹一下?HashMap。
當(dāng)一個(gè)節(jié)點(diǎn)中的鏈表超過(guò)了8的時(shí)候就會(huì)變?yōu)榧t黑樹,以解決鏈表長(zhǎng)度過(guò)長(zhǎng)以后查詢速度慢的缺點(diǎn)。
HashMap是由數(shù)組和鏈表組成的高效存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)。那么是如何確定一個(gè)數(shù)據(jù)存儲(chǔ)在數(shù)組中的哪個(gè)位置呢?就是通過(guò)?hashCode方法進(jìn)行計(jì)算出存儲(chǔ)在哪個(gè)位置,還記得我們上面講?hashCode方法說(shuō)了有可能兩個(gè)不同對(duì)象的?hashCode方法返回的值相同,那么此時(shí)就會(huì)產(chǎn)生沖突,產(chǎn)生沖突的話就會(huì)調(diào)用?equals方法進(jìn)行比對(duì),如果不同,那么就將其加入鏈表尾部,如果相同就替換原數(shù)據(jù)。
計(jì)算位置當(dāng)然不是上面簡(jiǎn)單的一個(gè)?hashCode方法就計(jì)算出來(lái),中間還有一些其他的步驟,這里可以簡(jiǎn)單的認(rèn)為是?hashCode確定了位置。
什么時(shí)候去覆蓋這兩個(gè)方法呢?
如果你不將自定義的類定義為?HashMap的key值的話,那么我們重寫了?equals方法而沒有重寫?hashCode方法,編譯器不會(huì)報(bào)任何錯(cuò),在運(yùn)行時(shí)也不會(huì)拋任何異常。
如果你想將自定義的類定義為?HashMap的key值得話,那么如果重寫了?equals?方法那么就必須也重寫?hashCode方法。
接下來(lái)我們可以看一下我們使用自定義的類作為?HashMap的key,并且自定義的類不重寫?equals和?hashCode方法會(huì)發(fā)生什么。
自定義的類
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
private Integer id;
private String name;
}
接下來(lái)我們看使用自定義的類作為key
public static void main(String[] args) {
Map<CustomizedKey, Integer> data = getData();
CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
Integer integer = data.get(key);
System.out.printf(String.valueOf(integer));
}
private static Map<CustomizedKey,Integer> getData(){
Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
customizedKeyIntegerMap.put(key,10);
return customizedKeyIntegerMap;
}
我們可以看到程序最后打印的是一個(gè)?null值。原因正如上面我們說(shuō)的一樣。
hashCode:用來(lái)計(jì)算該對(duì)象放入數(shù)組中的哪個(gè)位置,因?yàn)槭莾蓚€(gè)都是new的對(duì)象,所以即使里面的值一樣,但是對(duì)象所處的地址卻不同,所以使用默認(rèn)的?hashCode也就不同,當(dāng)然在?hashMap中就不會(huì)認(rèn)為兩個(gè)是一個(gè)對(duì)象。
接下來(lái)我們就重寫一下這兩個(gè)方法。如果我們使用?IDEA的話,那么直接使用快捷鍵即可。
接下來(lái)我們看我們實(shí)現(xiàn)的兩個(gè)方法
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
private Integer id;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomizedKey that = (CustomizedKey) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
然后我們?cè)俅芜\(yùn)行上面的程序發(fā)現(xiàn)輸出打印已經(jīng)變成了?10。
我們也能夠使用?Lombock提供的?@EqualsAndHashCode注解簡(jiǎn)化代碼
代碼地址:https://github.com/modouxiansheng/Doraemon
(完)
MarkerHub文章索引:
https://github.com/MarkerHub/JavaIndex
【推薦閱讀】
知乎問答:搞開發(fā)就怕加班還學(xué)不到東西?
很全很牛逼,看完這篇Elasticsearch實(shí)戰(zhàn),我覺得我可以寫個(gè)百度~
基于Jwt資源無(wú)狀態(tài)認(rèn)證權(quán)限管理系統(tǒng)bootshiro
別用Date了,Java8新特性之日期處理,現(xiàn)在學(xué)會(huì)也不遲!
eblog項(xiàng)目講解視頻上線啦,長(zhǎng)達(dá)17個(gè)小時(shí)!!
好文章!點(diǎn)個(gè)在看!總結(jié)
以上是生活随笔為你收集整理的float foo=42e1为什么错_为什么重写了equals()也要重写hashCode()的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Orchard中使用Image Gal
- 下一篇: Node.js Undocumented