sparta-紫杉 11/4/21 15:21
在對《Set和hashCode()》的一篇原創(chuàng)文章寫完后,由于對自己的一些論斷產(chǎn)生了模糊和懷疑,因此又對Set進行了一些研究,形成本篇。
在Set的使用場景中,我們不外乎看中了她存儲數(shù)據(jù)的唯一性,即不能存儲重復值,這在某些應用場合下是很必要的一個特性。那么從更深一層來考慮,Set究竟如何使數(shù)據(jù)不重復的呢? 從另一個層面來考慮,她又如何確保在驗證數(shù)據(jù)是否重復過程中的快速性呢? 假設存儲在Set中的數(shù)據(jù)有很多條,很普通的一個實現(xiàn)是每放入Set一個值value1,便提取Set中已有的多條數(shù)據(jù)跟該value1值進行對比,若相同,則不允許放入,反之則放入。運氣好的話,迭代到幾個元素之后便能夠判斷該值是否重復,否則,將會迭代所有已有的值。若是該Set中已經(jīng)存儲了上萬條的數(shù)據(jù),或者十幾萬條的數(shù)據(jù)?
一篇網(wǎng)文《Set是如何實現(xiàn)沒有重復元素的?》一文給出了答案,該文內(nèi)容翔實,說理性強,論據(jù)充分,是一篇難得的好文,在此感謝作者。尤其它指出了在Set的實現(xiàn)中,使用了Map的key唯一性來確保Set的值不重復(眾所周知,Map中的key是唯一的),有興趣的可以去查看一下。
那么,Map中的key又是如何確保重復驗證的快速性及key值的唯一性呢?
放心,這難不倒聰明的java的創(chuàng)造者們。他們巧妙地利用了Hash算法來實現(xiàn)并達到這一目的。 那么Hash又是什么?
Hash算法又稱為散列算法,其實Hash算法產(chǎn)生的目的很單純,其發(fā)明的目的是提高海量數(shù)據(jù)的查找速度。舉個實例更能說明問題:
假設數(shù)據(jù)表中有N個無序的字符串(例如:中文人名),給你一個字符串,請迅速找到它在數(shù)據(jù)表中的序號。最笨的方法是逐個比較的方式來查找。查找時間是O(N),簡單說最后的情況是比較N次。
hash 表能夠加快查找速度。使用hash表首先要申請一個定長的指針數(shù)組。通過在建立數(shù)據(jù)表時通過特定的計算公式(hash散列函數(shù))計算出每個字符串對應的一個數(shù)值(就是將不固定長度的字符串轉(zhuǎn)換成一個固定長度的數(shù)值或索引值)。而后把此數(shù)值作為數(shù)組下標,把此字符串在數(shù)據(jù)表的序號保存在此數(shù)組元素中(可以擴展到保存一個對象實例指針)。將來想查找某字符串對應位置時,只需要通過hash散列函數(shù)計算出字符串對應的值就可以直接知道此字符串的序號等信息了。 這樣查找時間是O(1)了。因為不需要查找了,知道數(shù)組下標就能訪問數(shù)組相應元素了,而元素中保存的就是序號等信息。
不妨給你一個直觀的比方吧:
張三,給你個任務,你到山東省東營區(qū)去找一下一個叫做“李四”的人吧。張三很老實,說了聲是就走了,三個月后回來,終于找著了那個叫做李四的人。他后來跟我們說,他采用了遍歷的手法,即挨家挨戶的去問,去找。
而將這個任務分配給王五的時候,王五說,老板,你給個確切的地址吧。老板說:山東省東營市東營區(qū)xx街道辦事處xx社區(qū)xx號。結果不到一天時間,王五便找著了。
這也許就是hash查找算法與普通查找算法的區(qū)別。
通過查看HashMap的源代碼證實,該HashMap確實采用了Hash算法來提高查找key的速度,并且使用了equals()來判斷是否重復,有代碼為證:
hash:
put:
HashMap的put原理是這樣的:1、首先對key采用hashCode()方法進行散列化,就是將key轉(zhuǎn)換生成一個int值,相同的key肯定會生成相同的int值,并對該int值進行hash計算得到hash值。2、通過hash值得到Entry數(shù)組的下標,然后通過該下標,得到已經(jīng)存入的數(shù)據(jù),將已經(jīng)存入的數(shù)據(jù)的key和hash進行比對,若相同證明是重復,則忽略。3、若不相同,則通過addentry()方法將數(shù)據(jù)存入該數(shù)組的下標中,同時存入的還有key、hash值。
用一句話說來就是,通過待存入的key的hash值計算出數(shù)組的下標,并根據(jù)該下標提取已經(jīng)存入的值,將兩者進行比對,若相同則忽略,不同則put進去。
現(xiàn)在知道為什么Map中的key是唯一性的原因了吧? 這與Map在put時兢兢業(yè)業(yè)檢查的努力是分不開的。
假設有一個key為1,value為"zhangSan",經(jīng)過hash之后成為101,那么這個101就作為數(shù)組的下標,然后將hash=101、key=1及value="zhangSan"的值封裝成實體對象存放到該數(shù)組的101下標處。因為不同的key會產(chǎn)生不同的hash值,這也是為什么HashMap不排序的原因!get:
那么通過key提取值時,當然先要通過該key計算出hash值來,再通過這個hash值作為下標提取出對應的實體對象所容納的value來。同時加了必要的判斷來確保提取出正確的數(shù)值來。
哈哈,在一個確定的城市里,領到了一個確定的門牌號,相比在茫茫人海中漫無目的的撈針,知道為何提取數(shù)據(jù)如何之快了吧!
posted on 2011-05-18 16:15 sparta-紫杉 閱讀(2660) 評論(0) 編輯 收藏 所屬分類: Java
Powered by: BlogJava Copyright © sparta-紫杉