摘要:根據的重新計算值。如果這兩個的通過比較返回,新添加的將覆蓋集合中原有的,但不會覆蓋。如果這兩個的通過比較返回,新添加的將與集合中原有形成鏈,而且新添加的位于鏈的頭部具體說明繼續看方法的說明。
關于hashCode
hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結構中確定對象的存儲地址的.
1.hashcode是用來查找的,如果你學過數據結構就應該知道,在查找和排序這一章有理解了hashCode我們來理解HashMap
例如內存中有這樣的位置
0 1 2 3 4 5 6 7
而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那么當查找時就需要到這八個位置里挨個去找,或者用二分法一類的算法。
但如果用hashcode那就會使效率提高很多。
我們這個類中有個字段叫ID,那么我們就定義我們的hashcode為ID%8,然后把我們的類存放在取得得余數那個位置。比如我們的ID為9,9除8的余數為1,那么我們就把該類存在1這個位置,如果ID是13,求得的余數是5,那么我們就把該類放在5這個位置。這樣,以后在查找該類時就可以通過ID除 8求余數直接找到存放的位置了。
2.但是如果兩個類有相同的hashcode怎么辦那(我們假設上面的類的ID不是唯一的),例如9除以8和17除以8的余數都是1,那么這是不是合法的,回答是:可以這樣。那么如何判斷呢?在這個時候就需要定義 equals了。
也就是說,我們先通過 hashcode來判斷兩個類是否存放某個桶里,但這個桶里可能有很多類,那么我們就需要再通過 equals 來在這個桶里找到我們要的類。
那么。重寫了equals(),為什么還要重寫hashCode()呢?
想想,你要在一個桶里找東西,你必須先要找到這個桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什么用啊
HashMap是基于哈希表的Map接口的非同步實現。此實現提供所有可選的映射操作,并允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恒久不變。
在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。
HashMap的內部存儲是一個數組(bucket),數組的元素Node實現了是Map.Entry接口(hash, key, value, next),next非空時指向定位相同的另一個Entry,如圖:
關于hashCodehashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結構中確定對象的存儲地址的.
1.hashcode是用來查找的,如果你學過數據結構就應該知道,在查找和排序這一章有理解了hashCode我們來理解HashMap
例如內存中有這樣的位置
0 1 2 3 4 5 6 7
而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那么當查找時就需要到這八個位置里挨個去找,或者用二分法一類的算法。
但如果用hashcode那就會使效率提高很多。
我們這個類中有個字段叫ID,那么我們就定義我們的hashcode為ID%8,然后把我們的類存放在取得得余數那個位置。比如我們的ID為9,9除8的余數為1,那么我們就把該類存在1這個位置,如果ID是13,求得的余數是5,那么我們就把該類放在5這個位置。這樣,以后在查找該類時就可以通過ID除 8求余數直接找到存放的位置了。
2.但是如果兩個類有相同的hashcode怎么辦那(我們假設上面的類的ID不是唯一的),例如9除以8和17除以8的余數都是1,那么這是不是合法的,回答是:可以這樣。那么如何判斷呢?在這個時候就需要定義 equals了。
也就是說,我們先通過 hashcode來判斷兩個類是否存放某個桶里,但這個桶里可能有很多類,那么我們就需要再通過 equals 來在這個桶里找到我們要的類。
那么。重寫了equals(),為什么還要重寫hashCode()呢?
想想,你要在一個桶里找東西,你必須先要找到這個桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什么用啊
HashMap是基于哈希表的Map接口的非同步實現。此實現提供所有可選的映射操作,并允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恒久不變。
在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。
HashMap的內部存儲是一個數組(bucket),數組的元素Node實現了是Map.Entry接口(hash, key, value, next),next非空時指向定位相同的另一個Entry,如圖:
HashMap實現存儲和讀取public V put(K key, V value) { // HashMap允許存放null鍵和null值。 // 當key為null時,調用putForNullKey方法,將value放置在數組第一個位置。 if (key == null) return putForNullKey(value); // 根據key的keyCode重新計算hash值。 int hash = hash(key.hashCode()); // 搜索指定hash值在對應table中的索引。 int i = indexFor(hash, table.length); // 如果 i 索引處的 Entry 不為 null,通過循環不斷遍歷 e 元素的下一個元素。 for (Entrye = 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; } } // 如果i索引處的Entry為null,表明此處還沒有Entry。 modCount++; // 將key、value添加到i索引處。 addEntry(hash, key, value, i); return null; }
根據hash值得到這個元素在數組中的位置(即下標),如果數組該位置上已經存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
hash(int h)方法根據key的hashCode重新計算一次散列。此算法加入了高位計算,防止低位不變,高位變化時,造成的hash沖突。
static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
HashMap中要找到某個元素,需要根據key的hash值來求得對應數組中的位置。如何計算這個位置就是hash算法。前面說過HashMap的數據結構是數組和鏈表的結合,所以我們當然希望這個HashMap里面的元素位置盡量的分布均勻些,盡量使得每個位置上的元素數量只有一個,那么當我們用hash算法求得這個位置的時候,馬上就可以知道對應位置的元素就是我們要的,而不用再去遍歷鏈表,這樣就大大優化了查詢的效率。
根據上面 put 方法的源代碼可以看出,當程序試圖將一個key-value對放入HashMap中時,程序首先根據該 key的 hashCode() 返回值決定該 Entry 的存儲位置:如果兩個 Entry 的 key 的 hashCode() 返回值相同,那它們的存儲位置相同。如果這兩個 Entry 的 key 通過 equals 比較返回 true,新添加 Entry 的 value 將覆蓋集合中原有 Entry的 value,但key不會覆蓋。如果這兩個 Entry 的 key 通過 equals 比較返回 false,新添加的 Entry 將與集合中原有 Entry 形成 Entry 鏈,而且新添加的 Entry 位于 Entry 鏈的頭部——具體說明繼續看 addEntry() 方法的說明。
JDK 源碼中 HashMap 的 hash 方法原理是什么?
public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entrye = 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中get元素時,首先計算key的hashCode,找到數組中對應位置的某一元素,然后通過key的equals方法在對應位置的鏈表中找到需要的元素。
當hashmap中的元素越來越多的時候,碰撞的幾率也就越來越高(因為數組的長度是固定的),所以為了提高查詢的效率,就要對hashmap的數組進行擴容,數組擴容這個操作也會出現在ArrayList中,所以這是一個通用的操作,很多人對它的性能表示過懷疑,不過想想我們的“均攤”原理,就釋然了,而在hashmap數組擴容之后,最消耗性能的點就出現了:原數組中的數據必須重新計算其在新數組中的位置,并放進去,這就是resize。
那么hashmap什么時候進行擴容呢?當hashmap中的元素個數超過數組大小loadFactor時,就會進行數組擴容,loadFactor的默認值為0.75,也就是說,默認情況下,數組大小為16,那么當hashmap中元素個數超過160.75=12的時候,就把數組的大小擴展為216=32,即擴大一倍,然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,所以如果我們已經預知hashmap中元素的個數,那么預設元素的個數能夠有效的提高hashmap的性能。比如說,我們有1000個元素new HashMap(1000), 但是理論上來講new HashMap(1024)更合適,不過上面annegu已經說過,即使是1000,hashmap也自動會將其設置為1024。 但是new HashMap(1024)還不是更合適的,因為0.751000 < 1000, 也就是說為了讓0.75 * size > 1000, 我們必須這樣new HashMap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。
Java8做的改變:
1.HashMap是數組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分),當鏈表長度>=8時轉化為紅黑樹
在JDK1.8版本中,對數據結構做了進一步的優化,引入了紅黑樹。而當鏈表長度太長(默認超過8)時,鏈表就轉換為紅黑樹,利用紅黑樹快速增刪改查的特點提高HashMap的性能,其中會用到紅黑樹的插入、刪除、查找等算法。
java8 中對hashmap護容不是重新計算所有元素在數組的位置,而是我們使用的是2次冪的擴展(指長度擴為原來2倍),所以,元素的位置要么是在原位置,要么是在原位置再移動2次冪的位置在擴充HashMap的時候,不需要像JDK1.7的實現那樣重新計算hash,只需要看看原來的hash值新增的那個bit是1還是0就好了,是0的話索引沒變,是1的話索引變成“原索引+oldCap"。
HashMap高并發情況下會出現什么問題?
擴容問題
HashMap的存放自定義類時,需要實現自定義類的什么方法?
hashCode和equals.通過hash(hashCode)然后模運算(其實是與的位操作)定位在Entry數組中的下標,然后遍歷這之后的鏈表,通過equals比較有沒有相同的key,如果有直接覆蓋value,如果沒有就重新創建一一個Entry。
hashmap為什么可以插入空值?
HashMap中添加key == null的Entry時會調用putForNullKey方法直接去遍歷table[0]Entry鏈表,尋找e.key == null的Entry或者沒有找到遍歷結束如果找到了e.key==null,就保存null值對應的原值oldValue,然后覆蓋原值,并返回oldValue如果在table[O]Entrty鏈表中沒有找到就調用addEntry方法添加一個key為null的Entry
Hashmap 為什么線程不安全(hash 碰撞和擴容導致)
HashMap擴容的的時候可能會形成環形鏈表,造成死循環。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73251.html
摘要:對象保存鍵值對。清空用于移除對象中指定的元素。執行刪除操作返回一個值,用來表明中是否存在指定元素一樣的后面的會覆蓋前面的值把對象轉換為迭代器返回一個新的對象對象是一組鍵值對的集合,其中的鍵是弱引用的。其鍵必須是對象,而值可以是任意的。 Map 對象保存鍵值對。任何值(對象或者原始值) 都可以作為一個鍵或一個值。 使用映射對象 let myMap=new Map(); let keyOb...
摘要:經過分析和思考,我決定不采用遞歸的方式來編寫樹形數據的處理,最終選用來維護樹節點之間的關系。以權限樹為例,做一個樹形數據工具類的設計。 1.簡介 ? 在一些管理系統中一般都會用到,會用到一些樹形數據,例如部門組織以及權限等數據,都得生成樹形數據,需要寫一些樹形數據生成工具,一般使用遞歸的方式,性能低下還可能會導致爆棧。經過分析和思考,我決定不采用遞歸的方式來編寫樹形數據的處理,最...
摘要:你有沒有感覺網上的教程很坑爹,你明明這樣做了,但是卻安裝不了你有沒有感覺網上的安裝教程有好幾個版本你是否曾興致沖沖地按照官網的教程亂搞一通,最終掃興而歸。 你有沒有感覺網上的Docker教程很坑爹,你明明這樣做了,但是卻安裝不了 你有沒有感覺網上的Docker安裝教程有好幾個版本; 你是否曾興致沖沖地按照官網的教程亂搞一通,最終掃興而歸。 方法一、安裝Ubuntu維護版,是dock...
閱讀 4028·2021-11-22 13:53
閱讀 1729·2021-09-23 11:52
閱讀 2445·2021-09-06 15:02
閱讀 955·2019-08-30 15:54
閱讀 911·2019-08-30 14:15
閱讀 2392·2019-08-29 18:39
閱讀 663·2019-08-29 16:07
閱讀 427·2019-08-29 13:13