日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅谈hashcode

發(fā)布時間:2025/5/22 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈hashcode 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

哈希表這個數(shù)據(jù)結(jié)構(gòu)想必大多數(shù)人都不陌生,而且在很多地方都會利用到hash表來提高查找效率。在Java的Object類中有一個方法:

1 public?native?int?hashCode();

  根據(jù)這個方法的聲明可知,該方法返回一個int類型的數(shù)值,并且是本地方法,因此在Object類中并沒有給出具體的實現(xiàn)。

  為何Object類需要這樣一個方法?它有什么作用呢?今天我們就來具體探討一下hashCode方法。

一.hashCode方法的作用

  對于包含容器類型的程序設(shè)計語言來說,基本上都會涉及到hashCode。在Java中也一樣,hashCode方法的主要作用是為了配合基于散列的集合一起正常運行,這樣的散列集合包括HashSet、HashMap以及HashTable。

  為什么這么說呢?考慮一種情況,當(dāng)向集合中插入對象時,如何判別在集合中是否已經(jīng)存在該對象了?(注意:集合中不允許重復(fù)的元素存在)

  也許大多數(shù)人都會想到調(diào)用equals方法來逐個進行比較,這個方法確實可行。但是如果集合中已經(jīng)存在一萬條數(shù)據(jù)或者更多的數(shù)據(jù),如果采用equals方法去逐一比較,效率必然是一個問題。此時hashCode方法的作用就體現(xiàn)出來了,當(dāng)集合要添加新的對象時,先調(diào)用這個對象的hashCode方法,得到對應(yīng)的hashcode值,實際上在HashMap的具體實現(xiàn)中會用一個table保存已經(jīng)存進去的對象的hashcode值,如果table中沒有該hashcode值,它就可以直接存進去,不用再進行任何比較了;如果存在該hashcode值, 就調(diào)用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址,所以這里存在一個沖突解決的問題,這樣一來實際調(diào)用equals方法的次數(shù)就大大降低了,說通俗一點:Java中的hashCode方法就是根據(jù)一定的規(guī)則將與對象相關(guān)的信息(比如對象的存儲地址,對象的字段等)映射成一個數(shù)值,這個數(shù)值稱作為散列值。下面這段代碼是java.util.HashMap的中put方法的具體實現(xiàn):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public?V put(K key, V value) { ????????if?(key ==?null) ????????????return?putForNullKey(value); ????????int?hash = hash(key.hashCode()); ????????int?i = indexFor(hash, table.length); ????????for?(Entry<K,V> e = table[i]; e !=?null; e = e.next) { ????????????Object k; ????????????if?(e.hash == hash && ((k = e.key) == key || key.equals(k))) { ????????????????V oldValue = e.value; ????????????????e.value = value; ????????????????e.recordAccess(this); ????????????????return?oldValue; ????????????} ????????} ????????modCount++; ????????addEntry(hash, key, value, i); ????????return?null; ????}

  put方法是用來向HashMap中添加新的元素,從put方法的具體實現(xiàn)可知,會先調(diào)用hashCode方法得到該元素的hashCode值,然后查看table中是否存在該hashCode值,如果存在則調(diào)用equals方法重新確定是否存在該元素,如果存在,則更新value值,否則將新的元素添加到HashMap中。從這里可以看出,hashCode方法的存在是為了減少equals方法的調(diào)用次數(shù),從而提高程序效率。

  如果對于hash表這個數(shù)據(jù)結(jié)構(gòu)的朋友不清楚,可以參考這幾篇博文;

  http://www.cnblogs.com/jiewei915/archive/2010/08/09/1796042.html

  http://www.cnblogs.com/dolphin0520/archive/2012/09/28/2700000.html

  http://www.java3z.com/cwbwebhome/article/article8/83560.html?id=4649

  有些朋友誤以為默認情況下,hashCode返回的就是對象的存儲地址,事實上這種看法是不全面的,確實有些JVM在實現(xiàn)時是直接返回對象的存儲地址,但是大多時候并不是這樣,只能說可能存儲地址有一定關(guān)聯(lián)。下面是HotSpot JVM中生成hash散列值的實現(xiàn):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 static?inline?intptr_t?get_next_hash(Thread * Self, oop obj) { ??intptr_t?value = 0 ; ??if?(hashCode == 0) { ?????// This form uses an unguarded global Park-Miller RNG, ?????// so it's possible for two threads to race and generate the same RNG. ?????// On MP system we'll have lots of RW access to a global, so the ?????// mechanism induces lots of coherency traffic. ?????value = os::random() ; ??}?else ??if?(hashCode == 1) { ?????// This variation has the property of being stable (idempotent) ?????// between STW operations.? This can be useful in some of the 1-0 ?????// synchronization schemes. ?????intptr_t?addrBits =?intptr_t(obj) >> 3 ; ?????value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ; ??}?else ??if?(hashCode == 2) { ?????value = 1 ;????????????// for sensitivity testing ??}?else ??if?(hashCode == 3) { ?????value = ++GVars.hcSequence ; ??}?else ??if?(hashCode == 4) { ?????value =?intptr_t(obj) ; ??}?else?{ ?????// Marsaglia's xor-shift scheme with thread-specific state ?????// This is probably the best overall implementation -- we'll ?????// likely make this the default in future releases. ?????unsigned t = Self->_hashStateX ; ?????t ^= (t << 11) ; ?????Self->_hashStateX = Self->_hashStateY ; ?????Self->_hashStateY = Self->_hashStateZ ; ?????Self->_hashStateZ = Self->_hashStateW ; ?????unsigned v = Self->_hashStateW ; ?????v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ; ?????Self->_hashStateW = v ; ?????value = v ; ??} ??value &= markOopDesc::hash_mask; ??if?(value == 0) value = 0xBAD ; ??assert?(value != markOopDesc::no_hash,?"invariant") ; ??TEVENT (hashCode: GENERATE) ; ??return?value; }

  該實現(xiàn)位于hotspot/src/share/vm/runtime/synchronizer.cpp文件下。

  因此有人會說,可以直接根據(jù)hashcode值判斷兩個對象是否相等嗎?肯定是不可以的,因為不同的對象可能會生成相同的hashcode值。雖然不能根據(jù)hashcode值判斷兩個對象是否相等,但是可以直接根據(jù)hashcode值判斷兩個對象不等,如果兩個對象的hashcode值不等,則必定是兩個不同的對象。如果要判斷兩個對象是否真正相等,必須通過equals方法。

  也就是說對于兩個對象,如果調(diào)用equals方法得到的結(jié)果為true,則兩個對象的hashcode值必定相等;

  如果equals方法得到的結(jié)果為false,則兩個對象的hashcode值不一定不同;

  如果兩個對象的hashcode值不等,則equals方法得到的結(jié)果必定為false;

  如果兩個對象的hashcode值相等,則equals方法得到的結(jié)果未知。

二.equals方法和hashCode方法

  在有些情況下,程序設(shè)計者在設(shè)計一個類的時候為需要重寫equals方法,比如String類,但是千萬要注意,在重寫equals方法的同時,必須重寫hashCode方法。為什么這么說呢?

  下面看一個例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package?com.cxh.test1; import?java.util.HashMap; import?java.util.HashSet; import?java.util.Set; class?People{ ????private?String name; ????private?int?age; ????? ????public?People(String name,int?age) { ????????this.name = name; ????????this.age = age; ????}?? ????? ????public?void?setAge(int?age){ ????????this.age = age; ????} ????????? ????@Override ????public?boolean?equals(Object obj) { ????????// TODO Auto-generated method stub ????????return?this.name.equals(((People)obj).name) &&?this.age== ((People)obj).age; ????} } public?class?Main { ????public?static?void?main(String[] args) { ????????? ????????People p1 =?new?People("Jack",?12); ????????System.out.println(p1.hashCode()); ????????????? ????????HashMap<People, Integer> hashMap =?new?HashMap<People, Integer>(); ????????hashMap.put(p1,?1); ????????? ????????System.out.println(hashMap.get(new?People("Jack",?12))); ????} }

  在這里我只重寫了equals方法,也就說如果兩個People對象,如果它的姓名和年齡相等,則認為是同一個人。

  這段代碼本來的意愿是想這段代碼輸出結(jié)果為“1”,但是事實上它輸出的是“null”。為什么呢?原因就在于重寫equals方法的同時忘記重寫hashCode方法。

  雖然通過重寫equals方法使得邏輯上姓名和年齡相同的兩個對象被判定為相等的對象(跟String類類似),但是要知道默認情況下,hashCode方法是將對象的存儲地址進行映射。那么上述代碼的輸出結(jié)果為“null”就不足為奇了。原因很簡單,p1指向的對象和

  System.out.println(hashMap.get(new People("Jack", 12)));這句中的new People("Jack", 12)生成的是兩個對象,它們的存儲地址肯定不同。下面是HashMap的get方法的具體實現(xiàn):

1 2 3 4 5 6 7 8 9 10 11 12 13 public?V get(Object key) { ????????if?(key ==?null) ????????????return?getForNullKey(); ????????int?hash = hash(key.hashCode()); ????????for?(Entry<K,V> e = table[indexFor(hash, table.length)]; ?????????????e !=?null; ?????????????e = e.next) { ????????????Object k; ????????????if?(e.hash == hash && ((k = e.key) == key || key.equals(k))) ????????????????return?e.value; ????????} ????????return?null; ????}

  所以在hashmap進行g(shù)et操作時,因為得到的hashcdoe值不同(注意,上述代碼也許在某些情況下會得到相同的hashcode值,不過這種概率比較小,因為雖然兩個對象的存儲地址不同也有可能得到相同的hashcode值),所以導(dǎo)致在get方法中for循環(huán)不會執(zhí)行,直接返回null。

  因此如果想上述代碼輸出結(jié)果為“1”,很簡單,只需要重寫hashCode方法,讓equals方法和hashCode方法始終在邏輯上保持一致性。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package?com.cxh.test1; import?java.util.HashMap; import?java.util.HashSet; import?java.util.Set; class?People{ ????private?String name; ????private?int?age; ????? ????public?People(String name,int?age) { ????????this.name = name; ????????this.age = age; ????}?? ????? ????public?void?setAge(int?age){ ????????this.age = age; ????} ????? ????@Override ????public?int?hashCode() { ????????// TODO Auto-generated method stub ????????return?name.hashCode()*37+age; ????} ????? ????@Override ????public?boolean?equals(Object obj) { ????????// TODO Auto-generated method stub ????????return?this.name.equals(((People)obj).name) &&?this.age== ((People)obj).age; ????} } public?class?Main { ????public?static?void?main(String[] args) { ????????? ????????People p1 =?new?People("Jack",?12); ????????System.out.println(p1.hashCode()); ????????????? ????????HashMap<People, Integer> hashMap =?new?HashMap<People, Integer>(); ????????hashMap.put(p1,?1); ????????? ????????System.out.println(hashMap.get(new?People("Jack",?12))); ????} }

  這樣一來的話,輸出結(jié)果就為“1”了。

  下面這段話摘自Effective Java一書:

  • 在程序執(zhí)行期間,只要equals方法的比較操作用到的信息沒有被修改,那么對這同一個對象調(diào)用多次,hashCode方法必須始終如一地返回同一個整數(shù)。
  • 如果兩個對象根據(jù)equals方法比較是相等的,那么調(diào)用兩個對象的hashCode方法必須返回相同的整數(shù)結(jié)果。
  • 如果兩個對象根據(jù)equals方法比較是不等的,則hashCode方法不一定得返回不同的整數(shù)。

  對于第二條和第三條很好理解,但是第一條,很多時候就會忽略。在《Java編程思想》一書中的P495頁也有同第一條類似的一段話:

  “設(shè)計hashCode()時最重要的因素就是:無論何時,對同一個對象調(diào)用hashCode()都應(yīng)該產(chǎn)生同樣的值。如果在講一個對象用put()添加進HashMap時產(chǎn)生一個hashCdoe值,而用get()取出時卻產(chǎn)生了另一個hashCode值,那么就無法獲取該對象了。所以如果你的hashCode方法依賴于對象中易變的數(shù)據(jù),用戶就要當(dāng)心了,因為此數(shù)據(jù)發(fā)生變化時,hashCode()方法就會生成一個不同的散列碼”。

  下面舉個例子:

  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package?com.cxh.test1; import?java.util.HashMap; import?java.util.HashSet; import?java.util.Set; class?People{ ????private?String name; ????private?int?age; ????? ????public?People(String name,int?age) { ????????this.name = name; ????????this.age = age; ????}?? ????? ????public?void?setAge(int?age){ ????????this.age = age; ????} ????? ????@Override ????public?int?hashCode() { ????????// TODO Auto-generated method stub ????????return?name.hashCode()*37+age; ????} ????? ????@Override ????public?boolean?equals(Object obj) { ????????// TODO Auto-generated method stub ????????return?this.name.equals(((People)obj).name) &&?this.age== ((People)obj).age; ????} } public?class?Main { ????public?static?void?main(String[] args) { ????????? ????????People p1 =?new?People("Jack",?12); ????????System.out.println(p1.hashCode()); ????????? ????????HashMap<People, Integer> hashMap =?new?HashMap<People, Integer>(); ????????hashMap.put(p1,?1); ????????? ????????p1.setAge(13); ????????? ????????System.out.println(hashMap.get(p1)); ????} }

  這段代碼輸出的結(jié)果為“null”,想必其中的原因大家應(yīng)該都清楚了。

  因此,在設(shè)計hashCode方法和equals方法的時候,如果對象中的數(shù)據(jù)易變,則最好在equals方法和hashCode方法中不要依賴于該字段。

  以上屬個人理解,如有不正之處,歡迎批評指正。

作者:海子      出處:http://www.cnblogs.com/dolphin0520/      本博客中未標(biāo)明轉(zhuǎn)載的文章歸作者海子和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。

轉(zhuǎn)載于:https://www.cnblogs.com/ziq711/p/8193533.html

總結(jié)

以上是生活随笔為你收集整理的浅谈hashcode的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲美女影院 | 日本aaa视频| 国产精品久久久久久久久久 | 国产免费一区二区三区最新不卡 | 69av在线| 成人在线视频免费 | 日本天天色| 在线观看亚洲视频 | 狠狠干中文字幕 | 久久在线播放 | 天天干,天天爽 | 女女同性女同一区二区三区按摩 | 亚洲欧美在线一区二区 | 国产成人在线播放 | 无码人妻aⅴ一区二区三区有奶水 | 日本久久一区 | 在线播放波多野结衣 | av免费观看网站 | 超碰国产人人 | 色一情一乱一伦 | 亚洲国产欧美视频 | 成人黄色在线免费观看 | 五月婷婷综合在线 | 91丨porny丨成人蝌蚪 | 亚欧在线视频 | 午夜视频国产 | 国产精品一区二区三区久久 | 亚洲va久久久噜噜噜久久天堂 | 国产传媒第一页 | 越南a级片 | 色网在线看 | 无码av免费精品一区二区三区 | 婷婷五月小说 | 亚洲天天视频 | 欧美一区二区三区的 | 女儿的朋友5中汉字晋通话 欧美成人免费高清视频 | 日本一级三级三级三级 | 日韩大尺度视频 | 96在线视频| 米奇狠狠干 | 国产激情视频在线观看 | 欧美成人免费一级人片100 | 国产91区 | 免费二区 | 91丝袜一区在线观看 | 欧美三日本三级少妇三级99观看视频 | www.久久爱 | 91 高清 在线 制服 偷拍 | 黄色91在线观看 | 中文字幕在线免费播放 | 香蕉视频国产在线观看 | 欧美性猛交xxx乱大交3蜜桃 | 中文字幕一区二区三区日韩精品 | 国产成人在线播放视频 | 50一60岁老妇女毛片 | 九九看片 | 中文字幕久久网 | 中文字幕欧美人妻精品 | 免费久久av | 美国一级特黄 | 人人草人人射 | 中文字幕制服丝袜 | 麻豆成人网 | 樱桃视频污污 | 337p粉嫩大胆噜噜噜噜69影视 | 最新福利在线 | 亚洲精品男人天堂 | 亚洲美女中文字幕 | 国产老头老太作爱视频 | 日韩麻豆 | 日本在线| 日本少妇xxxxx | 六月激情综合网 | 丰满人妻一区二区三区53 | 好吊妞一区二区三区 | 国产精品第六页 | 嫩草研究院在线观看 | 91在线网址 | 国产又粗又硬 | 成人免费性生活视频 | 超碰狠狠干 | 特大黑人娇小亚洲女mp4 | 国产精品一区二区三区线羞羞网站 | 色狗网站 | 18被视频免费观看视频 | 午夜伦理在线观看 | 绝顶高潮合集videos | 91蝌蚪少妇偷拍 | 日韩三级国产精品 | 私拍在线 | 午夜成年人 | 天天视频污 | 美女网站免费观看视频 | 露脸丨91丨九色露脸 | 日韩精品av一区二区三区 | 国产精品字幕 | 久久91av | 艳妇乳肉亭妇荡乳av | 国产又粗又长又爽 |