日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

看完这篇 HashMap ,和面试官扯皮就没问题了

發(fā)布時(shí)間:2024/8/23 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 看完这篇 HashMap ,和面试官扯皮就没问题了 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

來(lái)源 | Java?建設(shè)者

責(zé)編 | Carol

封圖 | CSDN 下載自視覺中國(guó)

(如果你沒有時(shí)間細(xì)摳本文,可以直接看 HashMap 概述,能讓你對(duì) HashMap 有個(gè)大致的了解)

HashMap 是 Map 接口的實(shí)現(xiàn),HashMap 允許空的 key-value 鍵值對(duì),HashMap 被認(rèn)為是 Hashtable 的增強(qiáng)版,HashMap 是一個(gè)非線程安全的容器,如果想構(gòu)造線程安全的 Map 考慮使用 ConcurrentHashMap。HashMap 是無(wú)序的,因?yàn)?HashMap 無(wú)法保證內(nèi)部存儲(chǔ)的鍵值對(duì)的有序性。

HashMap 的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組 + 鏈表的集合體,數(shù)組在 HashMap 中又被稱為桶(bucket)。遍歷 HashMap 需要的時(shí)間損耗為 HashMap 實(shí)例桶的數(shù)量 + (key - value 映射) 的數(shù)量。因此,如果遍歷元素很重要的話,不要把初始容量設(shè)置的太高或者負(fù)載因子設(shè)置的太低。

HashMap 實(shí)例有兩個(gè)很重要的因素,初始容量和負(fù)載因子,初始容量指的就是 hash 表桶的數(shù)量,負(fù)載因子是一種衡量哈希表填充程度的標(biāo)準(zhǔn),當(dāng)哈希表中存在足夠數(shù)量的 entry,以至于超過(guò)了負(fù)載因子和當(dāng)前容量,這個(gè)哈希表會(huì)進(jìn)行 rehash 操作,內(nèi)部的數(shù)據(jù)結(jié)構(gòu)重新 rebuilt。

注意 HashMap 不是線程安全的,如果多個(gè)線程同時(shí)影響了 HashMap ,并且至少一個(gè)線程修改了 HashMap 的結(jié)構(gòu),那么必須對(duì) HashMap 進(jìn)行同步操作。可以使用?Collections.synchronizedMap(new HashMap)?來(lái)創(chuàng)建一個(gè)線程安全的 Map。

HashMap 會(huì)導(dǎo)致除了迭代器本身的 remove 外,外部 remove 方法都可能會(huì)導(dǎo)致 fail-fast 機(jī)制,因此盡量要用迭代器自己的 remove 方法。如果在迭代器創(chuàng)建的過(guò)程中修改了 map 的結(jié)構(gòu),就會(huì)拋出?ConcurrentModificationException?異常。

下面就來(lái)聊一聊 HashMap 的細(xì)節(jié)問(wèn)題。我們還是從面試題入手來(lái)分析 HashMap 。

HashMap 和 HashTable 的區(qū)別

我們上面介紹了一下 HashMap ,現(xiàn)在來(lái)介紹一下 HashTable。

相同點(diǎn)

HashMap 和 HashTable 都是基于哈希表實(shí)現(xiàn)的,其內(nèi)部每個(gè)元素都是?key-value?鍵值對(duì),HashMap 和 HashTable 都實(shí)現(xiàn)了 Map、Cloneable、Serializable 接口。

不同點(diǎn)

  • 父類不同:HashMap 繼承了?AbstractMap?類,而 HashTable 繼承了?Dictionary?類

  • 空值不同:HashMap 允許空的 key 和 value 值,HashTable 不允許空的 key 和 value 值。HashMap 會(huì)把 Null key 當(dāng)做普通的 key 對(duì)待。不允許 null key 重復(fù)。

  • 線程安全性:HashMap 不是線程安全的,如果多個(gè)外部操作同時(shí)修改 HashMap 的數(shù)據(jù)結(jié)構(gòu)比如 add 或者是 delete,必須進(jìn)行同步操作,僅僅對(duì) key 或者 value 的修改不是改變數(shù)據(jù)結(jié)構(gòu)的操作。可以選擇構(gòu)造線程安全的 Map 比如?Collections.synchronizedMap?或者是?ConcurrentHashMap。而 HashTable 本身就是線程安全的容器。

  • 性能方面:雖然 HashMap 和 HashTable 都是基于單鏈表的,但是 HashMap 進(jìn)行 put 或者 get???? 操作,可以達(dá)到常數(shù)時(shí)間的性能;而 HashTable 的 put 和 get 操作都是加了?synchronized?鎖的,所以效率很差。

  • 初始容量不同:HashTable 的初始長(zhǎng)度是11,之后每次擴(kuò)充容量變?yōu)橹暗?2n+1(n為上一次的長(zhǎng)度)

    而 HashMap 的初始長(zhǎng)度為16,之后每次擴(kuò)充變?yōu)樵瓉?lái)的兩倍。創(chuàng)建時(shí),如果給定了容量初始值,那么HashTable 會(huì)直接使用你給定的大小,而 HashMap 會(huì)將其擴(kuò)充為2的冪次方大小。

HashMap 和 HashSet 的區(qū)別

也經(jīng)常會(huì)問(wèn)到 HashMap 和 HashSet 的區(qū)別

HashSet 繼承于 AbstractSet 接口,實(shí)現(xiàn)了 Set、Cloneable,、java.io.Serializable 接口。HashSet 不允許集合中出現(xiàn)重復(fù)的值。HashSet 底層其實(shí)就是 HashMap,所有對(duì) HashSet 的操作其實(shí)就是對(duì) HashMap 的操作。所以 HashSet 也不保證集合的順序。

HashMap 底層結(jié)構(gòu)

要了解一個(gè)類,先要了解這個(gè)類的結(jié)構(gòu),先來(lái)看一下 HashMap 的結(jié)構(gòu):

最主要的三個(gè)類(接口)就是?HashMap,AbstractMap和?Map?了,HashMap 我們上面已經(jīng)在概述中簡(jiǎn)單介紹了一下,下面來(lái)介紹一下 AbstractMap。

AbstractMap 類

這個(gè)抽象類是 Map 接口的骨干實(shí)現(xiàn),以求最大化的減少實(shí)現(xiàn)類的工作量。為了實(shí)現(xiàn)不可修改的 map,程序員僅需要繼承這個(gè)類并且提供 entrySet 方法的實(shí)現(xiàn)即可。它將會(huì)返回一組 map 映射的某一段。通常,返回的集合將在AbstractSet 之上實(shí)現(xiàn)。這個(gè)set不應(yīng)該支持 add 或者 remove 方法,并且它的迭代器也不支持 remove 方法。

為了實(shí)現(xiàn)可修改的 map,程序員必須額外重寫這個(gè)類的 put 方法(否則就會(huì)拋出UnsupportedOperationException),并且 entrySet.iterator() 返回的 iterator 必須實(shí)現(xiàn) remove() 方法。

Map 接口

Map 接口定義了 key-value 鍵值對(duì)的標(biāo)準(zhǔn)。一個(gè)對(duì)象支持 key-value 存儲(chǔ)。Map不能包含重復(fù)的 key,每個(gè)鍵最多映射一個(gè)值。這個(gè)接口代替了Dictionary 類,Dictionary是一個(gè)抽象類而不是接口。

Map 接口提供了三個(gè)集合的構(gòu)造器,它允許將 map 的內(nèi)容視為一組鍵,值集合或一組鍵值映射。map的順序定義為map映射集合上的迭代器返回其元素的順序。一些map實(shí)現(xiàn),像是TreeMap類,保證了map的有序性;其他的實(shí)現(xiàn),像是HashMap,則沒有保證。

重要內(nèi)部類和接口

Node 接口

Node節(jié)點(diǎn)是用來(lái)存儲(chǔ)HashMap的一個(gè)個(gè)實(shí)例,它實(shí)現(xiàn)了?Map.Entry接口,我們先來(lái)看一下 Map中的內(nèi)部接口 Entry 接口的定義

Map.Entry

//?一個(gè)map?的entry?鏈,這個(gè)Map.entrySet()方法返回一個(gè)集合的視圖,包含類中的元素, //?這個(gè)唯一的方式是從集合的視圖進(jìn)行迭代,獲取一個(gè)map的entry鏈。這些Map.Entry鏈只在 //?迭代期間有效。 interface?Entry<K,V>?{K?getKey();V?getValue();V?setValue(V?value);boolean?equals(Object?o);int?hashCode(); }

Node 節(jié)點(diǎn)會(huì)存儲(chǔ)四個(gè)屬性,hash值,key,value,指向下一個(gè)Node節(jié)點(diǎn)的引用

?//?hash值 final?int?hash; //?鍵 final?K?key; //?值 V?value; //?指向下一個(gè)Node節(jié)點(diǎn)的Node類型 Node<K,V>?next;

因?yàn)镸ap.Entry 是一條條entry 鏈連接在一起的,所以Node節(jié)點(diǎn)也是一條條entry鏈。構(gòu)造一個(gè)新的HashMap實(shí)例的時(shí)候,會(huì)把這四個(gè)屬性值分為傳入

Node(int?hash,?K?key,?V?value,?Node<K,V>?next)?{this.hash?=?hash;this.key?=?key;this.value?=?value;this.next?=?next; }

實(shí)現(xiàn)了 Map.Entry 接口所以必須實(shí)現(xiàn)其中的方法,所以 Node 節(jié)點(diǎn)中也包括上面的五個(gè)方法

KeySet 內(nèi)部類

keySet 類繼承于 AbstractSet 抽象類,它是由 HashMap 中的?keyset()?方法來(lái)創(chuàng)建 KeySet 實(shí)例的,旨在對(duì)HashMap 中的key鍵進(jìn)行操作,看一個(gè)代碼示例

圖中把「1, 2, 3」這三個(gè)key 放在了HashMap中,然后使用 lambda 表達(dá)式循環(huán)遍歷 key 值,可以看到,map.keySet() 其實(shí)是返回了一個(gè) Set 接口,KeySet() 是在 Map 接口中進(jìn)行定義的,不過(guò)是被HashMap 進(jìn)行了實(shí)現(xiàn)操作,來(lái)看一下源碼就明白了

//?返回一個(gè)set視圖,這個(gè)視圖中包含了map中的key。 public?Set<K>?keySet()?{//?//?keySet?指向的是?AbstractMap?中的?keysetSet<K>?ks?=?keySet;if?(ks?==?null)?{//?如果?ks?為空,就創(chuàng)建一個(gè)?KeySet?對(duì)象//?并對(duì) ks 賦值。ks?=?new?KeySet();keySet?=?ks;}return?ks; }

所以 KeySet 類中都是對(duì) Map中的 Key 進(jìn)行操作的:

Values 內(nèi)部類

Values 類的創(chuàng)建其實(shí)是和 KeySet 類很相似,不過(guò) KeySet 旨在對(duì) Map中的鍵進(jìn)行操作,Values 旨在對(duì)key-value?鍵值對(duì)中的 value 值進(jìn)行使用,看一下代碼示例:

循環(huán)遍歷 Map中的 values值,看一下 values() 方法最終創(chuàng)建的是什么:

public?Collection<V>?values()?{//?values?其實(shí)是?AbstractMap?中的?valuesCollection<V>?vs?=?values;if?(vs?==?null)?{vs?=?new?Values();values?=?vs;}return?vs; }

所有的 values 其實(shí)都存儲(chǔ)在 AbstractMap 中,而 Values 類其實(shí)也是實(shí)現(xiàn)了 Map 中的 Values 接口,看一下對(duì) values 的操作都有哪些方法

其實(shí)是和 key 的操作差不多

EntrySet 內(nèi)部類

上面提到了HashMap中分別有對(duì) key、value 進(jìn)行操作的,其實(shí)還有對(duì)?key-value?鍵值對(duì)進(jìn)行操作的內(nèi)部類,它就是 EntrySet,來(lái)看一下EntrySet 的創(chuàng)建過(guò)程:

點(diǎn)進(jìn)去 entrySet() 會(huì)發(fā)現(xiàn)這個(gè)方法也是在 Map 接口中定義的,HashMap對(duì)它進(jìn)行了重寫

//?返回一個(gè)?set?視圖,此視圖包含了?map?中的key-value?鍵值對(duì) public?Set<Map.Entry<K,V>>?entrySet()?{Set<Map.Entry<K,V>>?es;return?(es?=?entrySet)?==?null???(entrySet?=?new?EntrySet())?:?es; }

如果 es 為空創(chuàng)建一個(gè)新的 EntrySet 實(shí)例,EntrySet 主要包括了對(duì)key-value 鍵值對(duì)映射的方法,如下

HashMap 1.7 的底層結(jié)構(gòu)

JDK1.7 中,HashMap 采用位桶 + 鏈表的實(shí)現(xiàn),即使用鏈表來(lái)處理沖突,同一 hash 值的鏈表都存儲(chǔ)在一個(gè)數(shù)組中。但是當(dāng)位于一個(gè)桶中的元素較多,即 hash 值相等的元素較多時(shí),通過(guò) key 值依次查找的效率較低。它的數(shù)據(jù)結(jié)構(gòu)如下

HashMap 大致結(jié)構(gòu)

HashMap 底層數(shù)據(jù)結(jié)構(gòu)就是一個(gè) Entry 數(shù)組,Entry 是 HashMap 的基本組成單元,每個(gè) Entry 中包含一個(gè) key-value 鍵值對(duì)。

transient?Entry<K,V>[]?table?=?(Entry<K,V>[])?EMPTY_TABLE;

而每個(gè) Entry 中包含?「hash, key ,value」?屬性,它是 HashMap 的一個(gè)內(nèi)部類

static?class?Entry<K,V>?implements?Map.Entry<K,V>?{final?K?key;V?value;Entry<K,V>?next;int?hash;Entry(int?h,?K?k,?V?v,?Entry<K,V>?n)?{value?=?v;next?=?n;key?=?k;hash?=?h;}... }

所以,HashMap 的整體結(jié)構(gòu)就像下面這樣

HashMap 1.8 的底層結(jié)構(gòu)

與 JDK 1.7 相比,1.8 在底層結(jié)構(gòu)方面做了一些改變,當(dāng)每個(gè)桶中元素大于 8 的時(shí)候,會(huì)轉(zhuǎn)變?yōu)榧t黑樹,目的就是優(yōu)化查詢效率,JDK 1.8 重寫了?resize()?方法。

HashMap 重要屬性

「初始容量」

HashMap 的默認(rèn)初始容量是由?DEFAULT_INITIAL_CAPACITY?屬性管理的。

static?final?int?DEFAULT_INITIAL_CAPACITY?=?1?<<?4;

HashMap 的默認(rèn)初始容量是 1 << 4 = 16, << 是一個(gè)左移操作,它相當(dāng)于是

「最大容量」

HashMap 的最大容量是

static?final?int?MAXIMUM_CAPACITY?=?1?<<?30;

這里是不是有個(gè)疑問(wèn)?int 占用四個(gè)字節(jié),按說(shuō)最大容量應(yīng)該是左移 31 位,為什么 HashMap 最大容量是左移 30 位呢?因?yàn)樵跀?shù)值計(jì)算中,最高位也就是最左位的位?是代表著符號(hào)為,0 -> 正數(shù),1 -> 負(fù)數(shù),容量不可能是負(fù)數(shù),所以 HashMap 最高位只能移位到 2 ^ 30 次冪。

「默認(rèn)負(fù)載因子」

HashMap 的默認(rèn)負(fù)載因子是

static?final?float?DEFAULT_LOAD_FACTOR?=?0.75f;

float 類型所以用?.f?為單位,負(fù)載因子是和擴(kuò)容機(jī)制有關(guān),這里大致提一下,后面會(huì)細(xì)說(shuō)。擴(kuò)容機(jī)制的原則是當(dāng) HashMap 中存儲(chǔ)的數(shù)量 > HashMap 容量 * 負(fù)載因子時(shí),就會(huì)把 HashMap 的容量擴(kuò)大為原來(lái)的二倍。

HashMap 的第一次擴(kuò)容就在 DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR = 12 時(shí)進(jìn)行。

「樹化閾值」

HashMap 的樹化閾值是

static?final?int?TREEIFY_THRESHOLD?=?8;

在進(jìn)行添加元素時(shí),當(dāng)一個(gè)桶中存儲(chǔ)元素的數(shù)量 > 8 時(shí),會(huì)自動(dòng)轉(zhuǎn)換為紅黑樹(JDK1.8 特性)。

「鏈表閾值」

HashMap 的鏈表閾值是

static?final?int?UNTREEIFY_THRESHOLD?=?6;

在進(jìn)行刪除元素時(shí),如果一個(gè)桶中存儲(chǔ)元素?cái)?shù)量 < 6 后,會(huì)自動(dòng)轉(zhuǎn)換為鏈表

「擴(kuò)容臨界值」

static?final?int?MIN_TREEIFY_CAPACITY?=?64;

這個(gè)值表示的是當(dāng)桶數(shù)組容量小于該值時(shí),優(yōu)先進(jìn)行擴(kuò)容,而不是樹化

「節(jié)點(diǎn)數(shù)組」

HashMap 中的節(jié)點(diǎn)數(shù)組就是 Entry 數(shù)組,它代表的就是 HashMap 中?「數(shù)組 + 鏈表」?數(shù)據(jù)結(jié)構(gòu)中的數(shù)組。

transient?Node<K,V>[]?table;

Node 數(shù)組在第一次使用的時(shí)候進(jìn)行初始化操作,在必要的時(shí)候進(jìn)行?resize,resize 后數(shù)組的長(zhǎng)度擴(kuò)容為原來(lái)的二倍。

「鍵值對(duì)數(shù)量」

在 HashMap 中,使用?size?來(lái)表示 HashMap 中鍵值對(duì)的數(shù)量。

「修改次數(shù)」

在 HashMap 中,使用?modCount?來(lái)表示修改次數(shù),主要用于做并發(fā)修改 HashMap 時(shí)的快速失敗 - fail-fast 機(jī)制。

「擴(kuò)容閾值」

在 HashMap 中,使用?threshold?表示擴(kuò)容的閾值,也就是 初始容量 * 負(fù)載因子的值。

threshold 涉及到一個(gè)擴(kuò)容的閾值問(wèn)題,這個(gè)問(wèn)題是由?tableSizeFor?源碼解決的。我們先看一下它的源碼再來(lái)解釋

static?final?int?tableSizeFor(int?cap)?{int?n?=?cap?-?1;n?|=?n?>>>?1;n?|=?n?>>>?2;n?|=?n?>>>?4;n?|=?n?>>>?8;n?|=?n?>>>?16;return?(n?<?0)???1?:?(n?>=?MAXIMUM_CAPACITY)???MAXIMUM_CAPACITY?:?n?+?1; }

代碼中涉及一個(gè)運(yùn)算符?|=?,它表示的是按位或,啥意思呢?你一定知道?「a+=b 的意思是 a=a+b」,那么同理:a |= b 就是 a = a | b,也就是雙方都轉(zhuǎn)換為二進(jìn)制,來(lái)進(jìn)行與操作。如下圖所示

我們上面采用了一個(gè)比較大的數(shù)字進(jìn)行擴(kuò)容,由上圖可知 2^29 次方的數(shù)組經(jīng)過(guò)一系列的或操作后,會(huì)算出來(lái)結(jié)果是 2^30 次方。

所以擴(kuò)容后的數(shù)組長(zhǎng)度是原來(lái)的 2 倍。

「負(fù)載因子」

loadFactor?表示負(fù)載因子,它表示的是 HashMap 中的密集程度。

HashMap 構(gòu)造函數(shù)

在 HashMap 源碼中,有四種構(gòu)造函數(shù),分別來(lái)介紹一下

  • 帶有初始容量 initialCapacity?和?負(fù)載因子 loadFactor?的構(gòu)造函數(shù)

public?HashMap(int?initialCapacity,?float?loadFactor)?{if?(initialCapacity?<?0)throw?new?IllegalArgumentException("Illegal?initial?capacity:?"?+initialCapacity);if?(initialCapacity?>?MAXIMUM_CAPACITY)initialCapacity?=?MAXIMUM_CAPACITY;if?(loadFactor?<=?0?||?Float.isNaN(loadFactor))throw?new?IllegalArgumentException("Illegal?load?factor:?"?+loadFactor);this.loadFactor?=?loadFactor;//?擴(kuò)容的閾值this.threshold?=?tableSizeFor(initialCapacity); }

初始容量不能為負(fù),所以當(dāng)傳遞初始容量 < 0 的時(shí)候,會(huì)直接拋出?IllegalArgumentException?異常。如果傳遞進(jìn)來(lái)的初始容量 > 最大容量時(shí),初始容量 = 最大容量。負(fù)載因子也不能小于 0 。然后進(jìn)行數(shù)組的擴(kuò)容,這個(gè)擴(kuò)容機(jī)制也非常重要,我們后面進(jìn)行探討

  • 只帶有 initialCapacity 的構(gòu)造函數(shù)

public?HashMap(int?initialCapacity)?{this(initialCapacity,?DEFAULT_LOAD_FACTOR); }

最終也會(huì)調(diào)用到上面的構(gòu)造函數(shù),不過(guò)這個(gè)默認(rèn)的負(fù)載因子就是 HashMap 的默認(rèn)負(fù)載因子也就是?0.75f

  • 無(wú)參數(shù)的構(gòu)造函數(shù)

public?HashMap()?{this.loadFactor?=?DEFAULT_LOAD_FACTOR; }

默認(rèn)的負(fù)載因子也就是 0.75f

  • 帶有 map 的構(gòu)造函數(shù)

public?HashMap(Map<??extends?K,???extends?V>?m)?{this.loadFactor?=?DEFAULT_LOAD_FACTOR;putMapEntries(m,?false); }

帶有 Map 的構(gòu)造函數(shù),會(huì)直接把外部元素批量放入 HashMap 中。

講一講 HashMap put 的全過(guò)程

我記得剛畢業(yè)一年去北京面試,一家公司問(wèn)我 HashMap put 過(guò)程的時(shí)候,我支支吾吾答不上來(lái),后面痛下決心好好整。以 JDK 1.8 為基準(zhǔn)進(jìn)行分析,后面也是。先貼出整段代碼,后面會(huì)逐行進(jìn)行分析。

final?V?putVal(int?hash,?K?key,?V?value,?boolean?onlyIfAbsent,boolean?evict)?{Node<K,V>[]?tab;?Node<K,V>?p;?int?n,?i;//?如果table?為null?或者沒有為?table?分配內(nèi)存,就resize一次if?((tab?=?table)?==?null?||?(n?=?tab.length)?==?0)n?=?(tab?=?resize()).length;//?指定hash值節(jié)點(diǎn)為空則直接插入,這個(gè)(n?-?1)?&?hash才是表中真正的哈希if?((p?=?tab[i?=?(n?-?1)?&?hash])?==?null)tab[i]?=?newNode(hash,?key,?value,?null);//?如果不為空else?{Node<K,V>?e;?K?k;//?計(jì)算表中的這個(gè)真正的哈希值與要插入的key.hash相比if?(p.hash?==?hash?&&((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))e?=?p;//?若不同的話,并且當(dāng)前節(jié)點(diǎn)已經(jīng)在?TreeNode?上了else?if?(p?instanceof?TreeNode)//?采用紅黑樹存儲(chǔ)方式e?=?((TreeNode<K,V>)p).putTreeVal(this,?tab,?hash,?key,?value);//?key.hash?不同并且也不再?TreeNode?上,在鏈表上找到?p.next==nullelse?{for?(int?binCount?=?0;?;?++binCount)?{if?((e?=?p.next)?==?null)?{//?在表尾插入p.next?=?newNode(hash,?key,?value,?null);//?新增節(jié)點(diǎn)后如果節(jié)點(diǎn)個(gè)數(shù)到達(dá)閾值,則進(jìn)入?treeifyBin()?進(jìn)行再次判斷if?(binCount?>=?TREEIFY_THRESHOLD?-?1)?//?-1?for?1sttreeifyBin(tab,?hash);break;}//?如果找到了同?hash、key?的節(jié)點(diǎn),那么直接退出循環(huán)if?(e.hash?==?hash?&&((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))break;//?更新?p?指向下一節(jié)點(diǎn)p?=?e;}}//?map中含有舊值,返回舊值if?(e?!=?null)?{?//?existing?mapping?for?keyV?oldValue?=?e.value;if?(!onlyIfAbsent?||?oldValue?==?null)e.value?=?value;afterNodeAccess(e);return?oldValue;}}//?map調(diào)整次數(shù)?+?1++modCount;//?鍵值對(duì)的數(shù)量達(dá)到閾值,需要擴(kuò)容if?(++size?>?threshold)resize();afterNodeInsertion(evict);return?null; }

首先看一下?putVal?方法,這個(gè)方法是 final 的,如果你自已定義 HashMap 繼承的話,是不允許你自己重寫 put 方法的,然后這個(gè)方法涉及五個(gè)參數(shù)

  • hash -> put 放在桶中的位置,在 put 之前,會(huì)進(jìn)行 hash 函數(shù)的計(jì)算。

  • key -> 參數(shù)的 key 值

  • value -> 參數(shù)的 value 值

  • onlyIfAbsent -> 是否改變已經(jīng)存在的值,也就是是否進(jìn)行 value 值的替換標(biāo)志

  • evict -> 是否是剛創(chuàng)建 HashMap 的標(biāo)志

在調(diào)用到 putVal 方法時(shí),首先會(huì)進(jìn)行 hash 函數(shù)計(jì)算應(yīng)該插入的位置

public?V?put(K?key,?V?value)?{return?putVal(hash(key),?key,?value,?false,?true); }

哈希函數(shù)的源碼如下

static?final?int?hash(Object?key)?{int?h;return?(key?==?null)???0?:?(h?=?key.hashCode())?^?(h?>>>?16); }

首先先來(lái)理解一下 hash 函數(shù)的計(jì)算規(guī)則

Hash 函數(shù)

hash 函數(shù)會(huì)根據(jù)你傳遞的 key 值進(jìn)行計(jì)算,首先計(jì)算 key 的?hashCode?值,然后再對(duì) hashcode 進(jìn)行無(wú)符號(hào)右移操作,最后再和 hashCode 進(jìn)行異或 ^?操作。

?

>>>: 無(wú)符號(hào)右移操作,它指的是?「無(wú)符號(hào)右移,也叫邏輯右移,即若該數(shù)為正,則高位補(bǔ)0,而若該數(shù)為負(fù)數(shù),則右移后高位同樣補(bǔ)0」?,也就是不管是正數(shù)還是負(fù)數(shù),右移都會(huì)在空缺位補(bǔ) 0 。

?

在得到 hash 值后,就會(huì)進(jìn)行 put 過(guò)程。

首先會(huì)判斷 HashMap 中的 Node 數(shù)組是否為 null,如果第一次創(chuàng)建 HashMap 并進(jìn)行第一次插入元素,首先會(huì)進(jìn)行數(shù)組的 resize,也就是重新分配,這里還涉及到一個(gè)?resize()?擴(kuò)容機(jī)制源碼分析,我們后面會(huì)介紹。擴(kuò)容完畢后,會(huì)計(jì)算出 HashMap 的存放位置,通過(guò)使用?「( n - 1 ) & hash」?進(jìn)行計(jì)算得出。

然后會(huì)把這個(gè)位置作為數(shù)組的下標(biāo)作為存放元素的位置。如果不為空,那么計(jì)算表中的這個(gè)真正的哈希值與要插入的 key.hash 相比。如果哈希值相同,key-value 不一樣,再判斷是否是樹的實(shí)例,如果是的話,那么就把它插入到樹上。如果不是,就執(zhí)行尾插法在 entry 鏈尾進(jìn)行插入。

會(huì)根據(jù)桶中元素的數(shù)量判斷是鏈表還是紅黑樹。然后判斷鍵值對(duì)數(shù)量是否大于閾值,大于的話則進(jìn)行擴(kuò)容。

擴(kuò)容機(jī)制

在 Java 中,數(shù)組的長(zhǎng)度是固定的,這意味著數(shù)組只能存儲(chǔ)固定量的數(shù)據(jù)。但在開發(fā)的過(guò)程中,很多時(shí)候我們無(wú)法知道該建多大的數(shù)組合適。好在 HashMap 是一種自動(dòng)擴(kuò)容的數(shù)據(jù)結(jié)構(gòu),在這種基于變長(zhǎng)的數(shù)據(jù)結(jié)構(gòu)中,擴(kuò)容機(jī)制是非常重要的。

在 HashMap 中,閾值大小為桶數(shù)組長(zhǎng)度與負(fù)載因子的乘積。當(dāng) HashMap 中的鍵值對(duì)數(shù)量超過(guò)閾值時(shí),進(jìn)行擴(kuò)容。HashMap 中的擴(kuò)容機(jī)制是由?resize()?方法來(lái)實(shí)現(xiàn)的,下面我們就來(lái)一次認(rèn)識(shí)下。(貼出中文注釋,便于復(fù)制)

final?Node<K,V>[]?resize()?{Node<K,V>[]?oldTab?=?table;//?存儲(chǔ)old?table?的大小int?oldCap?=?(oldTab?==?null)???0?:?oldTab.length;//?存儲(chǔ)擴(kuò)容閾值int?oldThr?=?threshold;int?newCap,?newThr?=?0;if?(oldCap?>?0)?{//?如果old?table數(shù)據(jù)已達(dá)最大,那么threshold也被設(shè)置成最大if?(oldCap?>=?MAXIMUM_CAPACITY)?{threshold?=?Integer.MAX_VALUE;return?oldTab;}//?左移擴(kuò)大二倍,else?if?((newCap?=?oldCap?<<?1)?<?MAXIMUM_CAPACITY?&&oldCap?>=?DEFAULT_INITIAL_CAPACITY)//?擴(kuò)容成原來(lái)二倍newThr?=?oldThr?<<?1;?//?double?threshold}//?如果oldThr???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????!>?0else?if?(oldThr?>?0)?//?initial?capacity?was?placed?in?thresholdnewCap?=?oldThr;//?如果old?table?<=?0?并且?存儲(chǔ)的閾值?<=?0else?{???????????????//?zero?initial?threshold?signifies?using?defaultsnewCap?=?DEFAULT_INITIAL_CAPACITY;newThr?=?(int)(DEFAULT_LOAD_FACTOR?*?DEFAULT_INITIAL_CAPACITY);}//?如果擴(kuò)充閾值為0if?(newThr?==?0)?{//?擴(kuò)容閾值為?初始容量*負(fù)載因子float?ft?=?(float)newCap?*?loadFactor;newThr?=?(newCap?<?MAXIMUM_CAPACITY?&&?ft?<?(float)MAXIMUM_CAPACITY??(int)ft?:?Integer.MAX_VALUE);}//?重新給負(fù)載因子賦值threshold?=?newThr;//?獲取擴(kuò)容后的數(shù)組@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[]?newTab?=?(Node<K,V>[])new?Node[newCap];table?=?newTab;//?如果第一次進(jìn)行table?初始化不會(huì)走下面的代碼//?擴(kuò)容之后需要重新把節(jié)點(diǎn)放在新擴(kuò)容的數(shù)組中if?(oldTab?!=?null)?{for?(int?j?=?0;?j?<?oldCap;?++j)?{Node<K,V>?e;if?((e?=?oldTab[j])?!=?null)?{oldTab[j]?=?null;if?(e.next?==?null)newTab[e.hash?&?(newCap?-?1)]?=?e;else?if?(e?instanceof?TreeNode)//?重新映射時(shí),需要對(duì)紅黑樹進(jìn)行拆分((TreeNode<K,V>)e).split(this,?newTab,?j,?oldCap);else?{?//?preserve?orderNode<K,V>?loHead?=?null,?loTail?=?null;Node<K,V>?hiHead?=?null,?hiTail?=?null;Node<K,V>?next;//?遍歷鏈表,并將鏈表節(jié)點(diǎn)按原順序進(jìn)行分組do?{next?=?e.next;if?((e.hash?&?oldCap)?==?0)?{if?(loTail?==?null)loHead?=?e;elseloTail.next?=?e;loTail?=?e;}else?{if?(hiTail?==?null)hiHead?=?e;elsehiTail.next?=?e;hiTail?=?e;}}?while?((e?=?next)?!=?null);//?將分組后的鏈表映射到新桶中if?(loTail?!=?null)?{loTail.next?=?null;newTab[j]?=?loHead;}if?(hiTail?!=?null)?{hiTail.next?=?null;newTab[j?+?oldCap]?=?hiHead;}}}}}return?newTab; }

擴(kuò)容機(jī)制源碼比較長(zhǎng),我們耐心點(diǎn)進(jìn)行拆分

我們以 if...else if...else 邏輯進(jìn)行拆分,上面代碼主要做了這幾個(gè)事情

  • 判斷 HashMap 中的數(shù)組的長(zhǎng)度,也就是?(Node<K,V>[])oldTab.length()?,再判斷數(shù)組的長(zhǎng)度是否比最大的的長(zhǎng)度也就是 2^30 次冪要大,大的話直接取最大長(zhǎng)度,否則利用位運(yùn)算?<<擴(kuò)容為原來(lái)的兩倍

  • 如果數(shù)組長(zhǎng)度不大于0 ,再判斷擴(kuò)容閾值?threshold?是否大于 0 ,也就是看有無(wú)外部指定的擴(kuò)容閾值,若有則使用,這里需要說(shuō)明一下 threshold 何時(shí)是?oldThr > 0,因?yàn)?oldThr = threshold ,這里其實(shí)比較的就是 threshold,因?yàn)?HashMap 中的每個(gè)構(gòu)造方法都會(huì)調(diào)用?HashMap(initCapacity,loadFactor)?這個(gè)構(gòu)造方法,所以如果沒有外部指定 initialCapacity,初始容量使用的就是 16,然后根據(jù)?this.threshold = tableSizeFor(initialCapacity);?求得 threshold 的值。

  • 否則,直接使用默認(rèn)的初始容量和擴(kuò)容閾值,走 else 的邏輯是在 table 剛剛初始化的時(shí)候。

然后會(huì)判斷 newThr 是否為 0 ,筆者在剛開始研究時(shí)發(fā)現(xiàn)?newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);?一直以為這是常量做乘法,怎么會(huì)為 0 ,其實(shí)不是這部分的問(wèn)題,在于上面邏輯判斷中的擴(kuò)容操作,可能會(huì)導(dǎo)致位溢出。

導(dǎo)致位溢出的示例:oldCap = 2^28 次冪,threshold > 2 的三次方整數(shù)次冪。在進(jìn)入到?float ft = (float)newCap * loadFactor;?這個(gè)方法是 2^28 * 2^(3+n) 會(huì)直接 > 2^31 次冪,導(dǎo)致全部歸零。

「在擴(kuò)容后需要把節(jié)點(diǎn)放在新擴(kuò)容的數(shù)組中,這里也涉及到三個(gè)步驟」

  • 循環(huán)桶中的每個(gè) Node 節(jié)點(diǎn),判斷 Node[i] 是否為空,為空直接返回,不為空則遍歷桶數(shù)組,并將鍵值對(duì)映射到新的桶數(shù)組中。

  • 如果不為空,再判斷是否是樹形結(jié)構(gòu),如果是樹形結(jié)構(gòu)則按照樹形結(jié)構(gòu)進(jìn)行拆分,拆分方法在?split?方法中。

  • 如果不是樹形結(jié)構(gòu),則遍歷鏈表,并將鏈表節(jié)點(diǎn)按原順序進(jìn)行分組。

講一講 get 方法全過(guò)程

我們上面講了 HashMap 中的 put 方法全過(guò)程,下面我們來(lái)看一下?get?方法的過(guò)程,

public?V?get(Object?key)?{Node<K,V>?e;return?(e?=?getNode(hash(key),?key))?==?null???null?:?e.value; }final?Node<K,V>?getNode(int?hash,?Object?key)?{Node<K,V>[]?tab;?Node<K,V>?first,?e;?int?n;?K?k;//?找到真實(shí)的元素位置if?((tab?=?table)?!=?null?&&?(n?=?tab.length)?>?0?&&(first?=?tab[(n?-?1)?&?hash])?!=?null)?{//?總是會(huì)check?一下第一個(gè)元素if?(first.hash?==?hash?&&?//?always?check?first?node((k?=?first.key)?==?key?||?(key?!=?null?&&?key.equals(k))))return?first;//?如果不是第一個(gè)元素,并且下一個(gè)元素不是空的if?((e?=?first.next)?!=?null)?{//?判斷是否屬于?TreeNode,如果是?TreeNode?實(shí)例,直接從?TreeNode.getTreeNode?取if?(first?instanceof?TreeNode)return?((TreeNode<K,V>)first).getTreeNode(hash,?key);//?如果還不是?TreeNode?實(shí)例,就直接循環(huán)數(shù)組元素,直到找到指定元素位置do?{if?(e.hash?==?hash?&&((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))return?e;}?while?((e?=?e.next)?!=?null);}}return?null; }

來(lái)簡(jiǎn)單介紹下吧,首先會(huì)檢查 table 中的元素是否為空,然后根據(jù) hash 算出指定 key 的位置。然后檢查鏈表的第一個(gè)元素是否為空,如果不為空,是否匹配,如果匹配,直接返回這條記錄;如果匹配,再判斷下一個(gè)元素的值是否為 null,為空直接返回,如果不為空,再判斷是否是?TreeNode?實(shí)例,如果是 TreeNode 實(shí)例,則直接使用?TreeNode.getTreeNode?取出元素,否則執(zhí)行循環(huán),直到下一個(gè)元素為 null 位置。

getNode?方法有一個(gè)比較重要的過(guò)程就是?「(n - 1) & hash」,這段代碼是確定需要查找的桶的位置的,那么,為什么要 (n - 1) & hash 呢?

n 就是 HashMap 中桶的數(shù)量,這句話的意思也就是說(shuō) (n - 1) & hash 就是 (桶的容量 - 1) & hash

//?為什么?HashMap?的檢索位置是?(table.size?-?1)?&?hash public?static?void?main(String[]?args)?{Map<String,Object>?map?=?new?HashMap<>();//?debug?得知?1?的?hash?值算出來(lái)是?49map.put("1","cxuan");//?debug?得知?1?的?hash?值算出來(lái)是?50map.put("2","cxuan");//?debug?得知?1?的?hash?值算出來(lái)是?51map.put("3","cxuan");}

那么每次算完之后的 (n - 1) & hash ,依次為

也就是?「tab[(n - 1) & hash]」?算出的具體位置。

HashMap 的遍歷方式

HashMap 的遍歷,也是一個(gè)使用頻次特別高的操作

HashMap 遍歷的基類是?HashIterator,它是一個(gè) Hash 迭代器,它是一個(gè) HashMap 內(nèi)部的抽象類,它的構(gòu)造比較簡(jiǎn)單,只有三種方法,「hasNext 、 remove 和 nextNode」?方法,其中 nextNode 方法是由三種迭代器實(shí)現(xiàn)的

這三種迭代器就就是

  • KeyIterator?,對(duì) key 進(jìn)行遍歷

  • ValueIterator,對(duì) value 進(jìn)行遍歷

  • EntryIterator, 對(duì) Entry 鏈進(jìn)行遍歷

雖然說(shuō)看著迭代器比較多,但其實(shí)他們的遍歷順序都是一樣的,構(gòu)造也非常簡(jiǎn)單,都是使用?HashIterator?中的?nextNode?方法進(jìn)行遍歷

final?class?KeyIterator?extends?HashIteratorimplements?Iterator<K>?{public?final?K?next()?{?return?nextNode().key;?}}final?class?ValueIterator?extends?HashIteratorimplements?Iterator<V>?{public?final?V?next()?{?return?nextNode().value;?} }final?class?EntryIterator?extends?HashIteratorimplements?Iterator<Map.Entry<K,V>>?{public?final?Map.Entry<K,V>?next()?{?return?nextNode();?} }

HashIterator 中的遍歷方式

abstract?class?HashIterator?{Node<K,V>?next;????????//?下一個(gè)?entry?節(jié)點(diǎn)Node<K,V>?current;?????//?當(dāng)前?entry?節(jié)點(diǎn)int?expectedModCount;??//?fail-fast?的判斷標(biāo)識(shí)int?index;?????????????//?當(dāng)前槽HashIterator()?{expectedModCount?=?modCount;Node<K,V>[]?t?=?table;current?=?next?=?null;index?=?0;if?(t?!=?null?&&?size?>?0)?{?//?advance?to?first?entrydo?{}?while?(index?<?t.length?&&?(next?=?t[index++])?==?null);}}public?final?boolean?hasNext()?{return?next?!=?null;}final?Node<K,V>?nextNode()?{Node<K,V>[]?t;Node<K,V>?e?=?next;if?(modCount?!=?expectedModCount)throw?new?ConcurrentModificationException();if?(e?==?null)throw?new?NoSuchElementException();if?((next?=?(current?=?e).next)?==?null?&&?(t?=?table)?!=?null)?{do?{}?while?(index?<?t.length?&&?(next?=?t[index++])?==?null);}return?e;}public?final?void?remove()?{...} }

next 和 current 分別表示下一個(gè) Node 節(jié)點(diǎn)和當(dāng)前的 Node 節(jié)點(diǎn),HashIterator 在初始化時(shí)會(huì)遍歷所有的節(jié)點(diǎn)。下面我們用圖來(lái)表示一下他們的遍歷順序

你會(huì)發(fā)現(xiàn)?nextNode()?方法的遍歷方式和 HashIterator 的遍歷方式一樣,只不過(guò)判斷條件不一樣,構(gòu)造 HashIterator 的時(shí)候判斷條件是有沒有鏈表,桶是否為 null,而遍歷 nextNode 的判斷條件變?yōu)橄乱粋€(gè) node 節(jié)點(diǎn)是不是 null ,并且桶是不是為 null。

HashMap 中的移除方法

HashMap 中的移除方法也比較簡(jiǎn)單了,源碼如下

public?V?remove(Object?key)?{Node<K,V>?e;return?(e?=?removeNode(hash(key),?key,?null,?false,?true))?==?null??null?:?e.value; }final?Node<K,V>?removeNode(int?hash,?Object?key,?Object?value,boolean?matchValue,?boolean?movable)?{Node<K,V>[]?tab;?Node<K,V>?p;?int?n,?index;if?((tab?=?table)?!=?null?&&?(n?=?tab.length)?>?0?&&(p?=?tab[index?=?(n?-?1)?&?hash])?!=?null)?{Node<K,V>?node?=?null,?e;?K?k;?V?v;if?(p.hash?==?hash?&&((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))node?=?p;else?if?((e?=?p.next)?!=?null)?{if?(p?instanceof?TreeNode)node?=?((TreeNode<K,V>)p).getTreeNode(hash,?key);else?{do?{if?(e.hash?==?hash?&&((k?=?e.key)?==?key?||(key?!=?null?&&?key.equals(k))))?{node?=?e;break;}p?=?e;}?while?((e?=?e.next)?!=?null);}}if?(node?!=?null?&&?(!matchValue?||?(v?=?node.value)?==?value?||(value?!=?null?&&?value.equals(v))))?{if?(node?instanceof?TreeNode)((TreeNode<K,V>)node).removeTreeNode(this,?tab,?movable);else?if?(node?==?p)tab[index]?=?node.next;elsep.next?=?node.next;++modCount;--size;afterNodeRemoval(node);return?node;}}return?null; }

remove 方法有很多,最終都會(huì)調(diào)用到 removeNode 方法,只不過(guò)傳遞的參數(shù)值不同,我們拿 remove(object) 來(lái)演示一下。

首先會(huì)通過(guò) hash 來(lái)找到對(duì)應(yīng)的 bucket,然后通過(guò)遍歷鏈表,找到鍵值相等的節(jié)點(diǎn),然后把對(duì)應(yīng)的節(jié)點(diǎn)進(jìn)行刪除。

關(guān)于 HashMap 的面試題

HashMap 的數(shù)據(jù)結(jié)構(gòu)

JDK1.7 中,HashMap 采用位桶 + 鏈表的實(shí)現(xiàn),即使用鏈表來(lái)處理沖突,同一 hash 值的鏈表都存儲(chǔ)在一個(gè)數(shù)組中。但是當(dāng)位于一個(gè)桶中的元素較多,即 hash 值相等的元素較多時(shí),通過(guò) key 值依次查找的效率較低。

所以,與 JDK 1.7 相比,JDK 1.8 在底層結(jié)構(gòu)方面做了一些改變,當(dāng)每個(gè)桶中元素大于 8 的時(shí)候,會(huì)轉(zhuǎn)變?yōu)榧t黑樹,目的就是優(yōu)化查詢效率。

HashMap 的 put 過(guò)程

大致過(guò)程如下,首先會(huì)使用 hash 方法計(jì)算對(duì)象的哈希碼,根據(jù)哈希碼來(lái)確定在 bucket 中存放的位置,如果 bucket 中沒有 Node 節(jié)點(diǎn)則直接進(jìn)行 put,如果對(duì)應(yīng) bucket 已經(jīng)有 Node 節(jié)點(diǎn),會(huì)對(duì)鏈表長(zhǎng)度進(jìn)行分析,判斷長(zhǎng)度是否大于 8,如果鏈表長(zhǎng)度小于 8 ,在 JDK1.7 前會(huì)使用頭插法,在 JDK1.8 之后更改為尾插法。如果鏈表長(zhǎng)度大于 8 會(huì)進(jìn)行樹化操作,把鏈表轉(zhuǎn)換為紅黑樹,在紅黑樹上進(jìn)行存儲(chǔ)。

HashMap 為啥線程不安全

HashMap 不是一個(gè)線程安全的容器,不安全性體現(xiàn)在多線程并發(fā)對(duì) HashMap 進(jìn)行 put 操作上。如果有兩個(gè)線程 A 和 B ,首先 A 希望插入一個(gè)鍵值對(duì)到 HashMap 中,在決定好桶的位置進(jìn)行 put 時(shí),此時(shí) A 的時(shí)間片正好用完了,輪到 B 運(yùn)行,B 運(yùn)行后執(zhí)行和 A 一樣的操作,只不過(guò) B 成功把鍵值對(duì)插入進(jìn)去了。如果 A 和 B 插入的位置(桶)是一樣的,那么線程 A 繼續(xù)執(zhí)行后就會(huì)覆蓋 B 的記錄,造成了數(shù)據(jù)不一致問(wèn)題。

還有一點(diǎn)在于 HashMap 在擴(kuò)容時(shí),因 resize 方法會(huì)形成環(huán),造成死循環(huán),導(dǎo)致 CPU 飆高。

HashMap 是如何處理哈希碰撞的

HashMap 底層是使用位桶 + 鏈表實(shí)現(xiàn)的,位桶決定元素的插入位置,位桶是由 hash 方法決定的,當(dāng)多個(gè)元素的 hash 計(jì)算得到相同的哈希值后,HashMap 會(huì)把多個(gè) Node 元素都放在對(duì)應(yīng)的位桶中,形成鏈表,這種處理哈希碰撞的方式被稱為鏈地址法。

其他處理 hash 碰撞的方式還有?「開放地址法、rehash 方法、建立一個(gè)公共溢出區(qū)」這幾種方法。

HashMap 是如何 get 元素的

首先會(huì)檢查 table 中的元素是否為空,然后根據(jù) hash 算出指定 key 的位置。然后檢查鏈表的第一個(gè)元素是否為空,如果不為空,是否匹配,如果匹配,直接返回這條記錄;如果匹配,再判斷下一個(gè)元素的值是否為 null,為空直接返回,如果不為空,再判斷是否是?TreeNode?實(shí)例,如果是 TreeNode 實(shí)例,則直接使用?TreeNode.getTreeNode?取出元素,否則執(zhí)行循環(huán),直到下一個(gè)元素為 null 位置。

HashMap 和 HashTable 有什么區(qū)別

見上

HashMap 和 HashSet 的區(qū)別

見上

HashMap 是如何擴(kuò)容的

HashMap 中有兩個(gè)非常重要的變量,一個(gè)是?loadFactor?,一個(gè)是?threshold?,loadFactor 表示的就是負(fù)載因子,threshold 表示的是下一次要擴(kuò)容的閾值,當(dāng) threshold = loadFactor * 數(shù)組長(zhǎng)度時(shí),數(shù)組長(zhǎng)度擴(kuò)大位原來(lái)的兩倍,來(lái)重新調(diào)整 map 的大小,并將原來(lái)的對(duì)象放入新的 bucket 數(shù)組中。

HashMap 的長(zhǎng)度為什么是 2 的冪次方

這道題我想了幾天,之前和群里小伙伴們探討每日一題的時(shí)候,問(wèn)他們?yōu)槭裁?length%hash == (n - 1) & hash,它們說(shuō)相等的前提是 length 的長(zhǎng)度 2 的冪次方,然后我回了一句難道 length 還能不是 2 的冪次方嗎?其實(shí)是我沒有搞懂因果關(guān)系,因?yàn)?HashMap 的長(zhǎng)度是 2 的冪次方,所以使用余數(shù)來(lái)判斷在桶中的下標(biāo)。如果 length 的長(zhǎng)度不是 2 的冪次方,小伙伴們可以舉個(gè)例子來(lái)試試。

例如長(zhǎng)度為 9 時(shí)候,3 & (9-1) = 0,2 & (9-1) = 0 ,都在 0 上,碰撞了;

這樣會(huì)增大 HashMap 碰撞的幾率。

HashMap 線程安全的實(shí)現(xiàn)有哪些

因?yàn)?HashMap 不是一個(gè)線程安全的容器,所以并發(fā)場(chǎng)景下推薦使用?ConcurrentHashMap?,或者使用線程安全的 HashMap,使用?Collections?包下的線程安全的容器,比如說(shuō)

Collections.synchronizedMap(new?HashMap());

還可以使用 HashTable ,它也是線程安全的容器,基于 key-value 存儲(chǔ),經(jīng)常用 HashMap 和 HashTable 做比較就是因?yàn)?HashTable 的數(shù)據(jù)結(jié)構(gòu)和 HashMap 相同。

上面效率最高的就是 ConcurrentHashMap。

文章并沒有敘述太多關(guān)于紅黑樹的構(gòu)造、包含添加、刪除、樹化等過(guò)程,一方面是自己能力還達(dá)不到,一方面是關(guān)于紅黑樹的描述太過(guò)于占據(jù)篇幅,紅黑樹又是很大的一部分內(nèi)容,所以會(huì)考慮放在后續(xù)的紅黑樹進(jìn)行講解。

推薦閱讀
  • 淺談分布式存儲(chǔ)中的網(wǎng)絡(luò)通信

  • 138 張圖帶你 MySQL 入門!

  • 如何在 Kubernetes 上配置 Jenkins?

  • 突發(fā)!印度封禁抖音、微信、快手等 59 款中國(guó) App

  • 厲害!國(guó)內(nèi)大學(xué)生計(jì)算機(jī)編程第一人,一人挑戰(zhàn)一個(gè)隊(duì),百度最年輕 T10,現(xiàn)創(chuàng)業(yè)自動(dòng)駕駛

  • Balancer因通縮代幣STA遭遇閃電貸攻擊,價(jià)值50萬(wàn)美元資產(chǎn)被黑

  • 淺談分布式存儲(chǔ)中的網(wǎng)絡(luò)通信

真香,朕在看了!

總結(jié)

以上是生活随笔為你收集整理的看完这篇 HashMap ,和面试官扯皮就没问题了的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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

午夜a区 | 一区二区三区在线观看免费 | 成人小视频在线观看免费 | 亚洲精品国偷自产在线99热 | 韩日电影在线观看 | 久久久福利视频 | 在线免费观看黄网站 | 国产在线欧美日韩 | 久久久2o19精品 | 亚洲一区二区观看 | 国产精品美女久久久免费 | aav在线| 国产视频一二区 | 欧美精品日韩 | 韩国av一区二区三区 | 国产一级在线播放 | 免费人成在线观看网站 | 爱射综合 | 狠狠色狠狠色 | 国产小视频91 | 国内丰满少妇猛烈精品播 | 亚洲在线成人精品 | 亚洲日本va午夜在线电影 | 国产免费观看高清完整版 | 视频在线99re | 日韩在线视频一区二区三区 | 91成人免费观看视频 | 二区三区视频 | 久久99久久99精品 | 国产一级做a爱片久久毛片a | 四虎影视成人精品 | 国产成人精品午夜在线播放 | 久久人视频 | 人人澡人人模 | 深爱五月激情网 | 久草视频在线播放 | 久久影院精品 | 97超碰资源站 | 麻豆91视频 | 欧美 日韩 国产 中文字幕 | 国产精品视频免费在线观看 | 一本一道波多野毛片中文在线 | 国产打女人屁股调教97 | 超碰97免费观看 | 亚洲天堂网视频 | 久草香蕉在线视频 | 日韩一区二区三 | 九九九视频精品 | 久久夜色精品国产欧美乱极品 | 成人综合婷婷国产精品久久免费 | 日本久久免费视频 | 欧美福利视频一区 | 97超碰中文字幕 | 免费情趣视频 | 日韩理论在线 | 亚洲理论片 | 天天干夜夜夜 | 日本精品久久久久 | 欧美精品久久久久久久久久久 | 狠狠色丁香婷婷综合欧美 | 婷婷综合亚洲 | 爱av在线网| 中文字幕在线久一本久 | 久久久久伊人 | 日韩一区二区免费播放 | 久久久久久久久黄色 | 狠狠干.com | 麻豆国产视频 | 97在线观看免费观看高清 | 久久久久成人精品亚洲国产 | 日韩高清www | 99热精品久久 | 欧美成年人在线视频 | 777久久久 | 日韩在线视频一区二区三区 | 免费看成人片 | 亚洲a在线观看 | 亚洲精品影视 | 成年人看片 | 国产黄色a | 亚洲免费a| 美女很黄免费网站 | 国产欧美精品一区二区三区四区 | 亚洲永久av | 91久久精品一区二区二区 | 国产精品美女久久久网av | 456免费视频 | 激情婷婷亚洲 | 中文字幕无吗 | 国内精品久久久 | 久草在线 | 精品在线观看一区二区 | 日韩欧美精品在线观看视频 | 最近中文字幕在线播放 | 国产精品a久久久久 | 久久夜色精品国产欧美乱极品 | 91一区二区三区久久久久国产乱 | 青青草国产免费 | 日韩v欧美v日本v亚洲v国产v | 婷婷中文字幕在线观看 | 亚洲精品网址在线观看 | 国产91免费在线观看 | 国产精品美女网站 | 亚洲午夜精品在线观看 | 日韩一级网站 | 色噜噜狠狠狠狠色综合久不 | 成人黄色在线播放 | 伊人国产女 | 日本中文一区二区 | 欧美亚洲久久 | 亚洲国产中文字幕在线视频综合 | 久久久久久久久久久久久9999 | 亚洲永久免费av | 天天搞天天| 久久在线看 | 国产精品11| 国产成人91 | 91av蜜桃 | 国产精品综合久久久久久 | 国产操在线 | 天天射天天干天天 | 在线观看视频 | 69视频永久免费观看 | 国产免费亚洲高清 | av在线播放一区二区三区 | 日韩性久久 | 欧美日韩国产一二三区 | 亚洲精品国产综合99久久夜夜嗨 | 日韩免费观看高清 | 色综合久久中文综合久久牛 | 香蕉97视频观看在线观看 | 日韩免费一区二区 | 激情丁香综合五月 | 国产69精品久久久久99尤 | 在线观看黄色的网站 | 国产一性一爱一乱一交 | 99久久综合精品五月天 | 久久综合九色综合久久久精品综合 | 日本激情视频中文字幕 | 天天草天天爽 | 国产香蕉视频在线观看 | 欧美日韩免费网站 | 午夜免费久久看 | 国产亚洲在线观看 | 精品免费久久 | 日韩欧美在线观看一区二区三区 | 国产精品视频免费在线观看 | 麻豆视频在线免费观看 | 日韩,中文字幕 | 欧美久久久久久久久久 | 国产在线国产 | 操久在线 | 91av小视频 | 久久视频在线 | 日韩爱爱网站 | 精品国产一二三 | 美女精品国产 | 91麻豆传媒 | 91av在线免费视频 | 日日夜夜精品网站 | 亚洲一区二区91 | 91人网站 | 狠狠夜夜 | 精品影院 | 极品美女被弄高潮视频网站 | 97在线超碰 | 黄色影院在线观看 | 国产精品一区二区三区四 | 国产丝袜制服在线 | 一区二区三区四区精品 | 又长又大又黑又粗欧美 | 日本久久精品视频 | 久久无码av一区二区三区电影网 | 超黄视频网站 | 在线观看中文字幕亚洲 | 黄色一级大片在线观看 | 91九色丨porny丨丰满6 | 欧美精品三级 | 国产精品一区二区久久国产 | 日韩高清一区在线 | 久久久久二区 | 美女免费视频网站 | 午夜精品久久久久久久久久久 | 992tv人人网tv亚洲精品 | 97在线看片 | 超碰在线官网 | 精品国产1区2区3区 国产欧美精品在线观看 | 亚洲理论在线 | 国产精品一区专区欧美日韩 | 色综合天天综合网国产成人网 | 亚洲区另类春色综合小说校园片 | 久久99久久久久久 | 亚洲欧美一区二区三区孕妇写真 | 丁香花在线视频观看免费 | 国内精品美女在线观看 | 日韩高清不卡一区二区三区 | 中文在线中文资源 | 日韩中文字幕第一页 | 国产剧情亚洲 | 亚洲欧洲成人精品av97 | 久久永久免费 | sesese图片| 成人性生爱a∨ | 久久综合国产伦精品免费 | 久久精品久久国产 | 国产高清久久久 | 午夜久久福利影院 | 中文字幕免费久久 | 久久精品国产亚洲 | 天天曰天天射 | 免费av网站观看 | 一级做a爱片性色毛片www | 成人亚洲综合 | 99久久婷婷国产一区二区三区 | 久久综合加勒比 | 精品国产一区二区三区久久久蜜臀 | 亚洲精品国产电影 | 中文字幕中文字幕中文字幕 | 亚洲欧美婷婷六月色综合 | 亚洲高清国产视频 | 91精品国产福利在线观看 | 久久人人添人人爽添人人88v | 亚洲黄色成人网 | 色播99 | av高清影院 | 国产精品久久久久久五月尺 | 亚洲视频免费在线看 | 在线观看黄色的网站 | 91精品一区二区三区蜜臀 | 国产精品99久久久久久宅男 | 97免费 | 最近更新好看的中文字幕 | 亚洲精品大片www | 91在线精品播放 | 6080yy精品一区二区三区 | 成年人精品 | 免费av成人在线 | 国产精品一区二区久久久 | 日韩素人在线观看 | 国产精品国产三级国产 | 免费三级网 | 成年人黄色免费网站 | 亚洲视频精品在线 | 手机看片午夜 | 免费看av在线 | 中文一区在线观看 | 免费91麻豆精品国产自产在线观看 | 日韩在线免费小视频 | 午夜视频黄| www.久久久.com | 免费精品国产va自在自线 | 亚洲九九九在线观看 | 日韩区欧美久久久无人区 | 中文一区二区三区在线观看 | 国产亚洲精品久 | 91视频免费看片 | 欧美日韩成人一区 | 亚洲精品福利视频 | 欧美日韩一区二区久久 | 色综合国产 | 绯色av一区 | 久久精品理论 | 日韩欧美在线不卡 | a级成人毛片 | 欧美ⅹxxxxxx | 欧美另类tv | 中文国产在线观看 | 五月婷婷综 | 免费v片| 999男人的天堂 | 中文字幕 欧美性 | 四川bbb搡bbb爽爽视频 | 91尤物在线播放 | 欧美日韩一级久久久久久免费看 | 91网免费看 | 亚洲九九影院 | 国产最新在线视频 | 97精品国产91久久久久久久 | 国产99久久九九精品免费 | av+在线播放在线播放 | 黄色大片免费播放 | 99r在线 | 三级黄色网络 | 国产亚洲人成网站在线观看 | 久久精品99国产国产 | 久久电影日韩 | 国产亚洲精品无 | 欧美特一级 | 西西444www大胆高清视频 | 久久精品国产免费看久久精品 | 福利网在线 | 中文字幕中文字幕在线中文字幕三区 | 亚洲色图av | 中文字幕第一页在线播放 | 97精品国产97久久久久久粉红 | av天天色 | 人人艹视频 | 日韩欧美视频一区二区 | 亚洲免费婷婷 | 91麻豆产精品久久久久久 | 伊人中文在线 | 中文字幕在线观看日本 | 91九色在线观看视频 | 色综合久久久久久久久五月 | 久久一区二区三区日韩 | 亚洲精品国偷拍自产在线观看 | 亚洲欧美国内爽妇网 | 久久综合久色欧美综合狠狠 | 国产精品永久久久久久久久久 | 国产99亚洲 | 午夜 免费 | 免费黄色一区 | 国产黄色成人av | 在线观看韩日电影免费 | 日韩在线观看视频中文字幕 | 国产99免费视频 | 久九视频 | 黄色影院在线播放 | 99久久久久成人国产免费 | 久久久蜜桃| 狠狠色丁婷婷日日 | 精品国产一区二区三区久久久蜜月 | 亚洲精品2区| 午夜12点| 美女又爽又黄 | h文在线观看免费 | 97精品伊人| 一区二区激情 | 亚洲视频在线播放 | av中文字幕亚洲 | 国产五月色婷婷六月丁香视频 | 日韩大片免费观看 | 91中文字幕在线观看 | 中文字幕亚洲精品日韩 | 成人av电影在线观看 | 欧美先锋影音 | 天天操夜夜看 | 国产精品k频道 | 91精品国自产在线观看欧美 | 亚洲 欧美 成人 | 久久久久亚洲精品 | 在线视频免费观看 | 欧美aaa级片| 国产免费专区 | 亚洲一级久久 | 国产精品久久久久av | 成人在线观看网址 | 97操操操| 日韩天天综合 | 色婷婷六月 | 精品久久福利 | 五月婷婷在线综合 | 久久婷婷国产色一区二区三区 | 91视频免费播放 | 外国av网| h网站免费在线观看 | 婷婷六月天综合 | 日韩激情av在线 | 久久人人爽人人爽人人片 | 四虎影视4hu4虎成人 | 中文字幕高清免费日韩视频在线 | 国产精品久久久久四虎 | 日韩av免费一区二区 | 网址你懂的在线观看 | 精品久久久久久亚洲综合网站 | 免费国产在线视频 | 日本69hd| 亚洲视频一区二区三区在线观看 | 91精品欧美一区二区三区 | 国产又粗又猛又爽又黄的视频免费 | 午夜精品福利一区二区三区蜜桃 | 麻豆视频在线免费 | 91精品办公室少妇高潮对白 | 国产成人福利 | 成人手机在线视频 | 中文字幕4 | www国产亚洲精品久久网站 | 在线黄色国产 | 337p日本大胆噜噜噜噜 | 亚洲专区 国产精品 | 美女一区网站 | 精品久久久久久久久久岛国gif | 在线免费中文字幕 | 黄污网站在线观看 | 久久成人人人人精品欧 | 久久高清av | 精品美女久久久久 | 久久亚洲影视 | 久久久www成人免费精品 | 日本精品视频在线观看 | 国内精品久久久久久中文字幕 | 九色精品免费永久在线 | 五月天六月丁香 | 亚洲国产精品电影 | 水蜜桃亚洲一二三四在线 | 国产精品第7页 | 婷婷色资源 | 超碰97人人爱 | 丝袜精品视频 | 国产成人一区二区三区免费看 | 高清视频一区二区三区 | 国产成人精品一区二区三区在线观看 | 国产精品日韩精品 | 国产1区在线观看 | 日韩欧美电影 | 久久伦理 | 国产精品久久久久久妇 | 精品国产免费久久 | 中文字幕在线观看三区 | 亚洲第一区在线观看 | 亚洲午夜av电影 | 欧美成人91| 国产精品久久久久久一区二区三区 | 亚洲成人免费在线观看 | www欧美色| 久久久96 | 免费观看一级一片 | 久久精品国产99国产 | 91人人爽人人爽人人精88v | 午夜精品三区 | 国产视频欧美视频 | 午夜精品久久久久99热app | 国产一区二区免费看 | 午夜影院在线观看18 | 欧美一进一出抽搐大尺度视频 | 国产精品免费观看网站 | 久久婷婷国产色一区二区三区 | 亚洲黄色成人网 | 久久视频在线视频 | 精品91| 伊人五月天婷婷 | 精品一区二区av | 亚洲码国产日韩欧美高潮在线播放 | 国产免费大片 | 香蕉蜜桃视频 | 最新精品视频在线 | 456成人精品影院 | 天天操天天射天天爽 | 久久亚洲综合国产精品99麻豆的功能介绍 | 最新av在线播放 | 国产午夜一级毛片 | 成人手机在线视频 | 亚洲免费国产 | 久久乐九色婷婷综合色狠狠182 | 五月婷婷综合在线视频 | 美女视频黄免费 | 成年人黄色大片在线 | 成年人电影免费看 | 国产专区在线播放 | 四虎国产精 | 久久a v视频 | 九七在线视频 | 99精品在线观看视频 | 一区二区三区视频在线 | 亚洲aⅴ乱码精品成人区 | 在线播放 亚洲 | 99se视频在线观看 | 精品国产一区二区三区久久 | 久要激情网 | 国产黄色在线观看 | 天堂资源在线观看视频 | 91免费版在线 | 欧美大荫蒂xxx | 夜夜高潮夜夜爽国产伦精品 | 亚洲精品国产综合久久 | 日本大片免费观看在线 | 在线亚洲观看 | 午夜12点| 日韩午夜精品 | 国产 av 日韩| 国产精品久久亚洲 | 中文免费 | 五月婷亚洲| 久草香蕉在线 | 国产一级二级在线播放 | 一级片免费视频 | 日韩av电影中文字幕 | 日本中文在线播放 | 久久成人一区二区 | 97看片网 | 国产亚洲精品久久久久久移动网络 | 天天干,天天射,天天操,天天摸 | av天天草 | 午夜视频在线观看网站 | 久久久久亚洲国产 | 国产黄色片免费看 | 色国产精品一区在线观看 | 91看片一区二区三区 | 欧美日韩国产成人 | 天天干天天干天天干天天干天天干天天干 | 色九九视频 | 一区二区三区 亚洲 | 国产精品久久久久久吹潮天美传媒 | 国产精华国产精品 | 日韩在线视频一区二区三区 | 在线播放 日韩专区 | 日韩免费电影在线观看 | 中文在线√天堂 | 久久精品国产亚洲精品2020 | av在线h| 日韩乱色精品一区二区 | 92av视频 | 亚洲激情国产精品 | 人人超在线公开视频 | 99久热在线精品视频成人一区 | 国产成人综 | 久久精品xxx| 五月在线 | 综合国产在线 | 欧美另类调教 | 91探花视频 | 久草免费在线视频 | 激情婷婷在线 | 国外成人在线视频网站 | 国产黄色片网站 | 97国产情侣爱久久免费观看 | 国产精品剧情在线亚洲 | 天堂在线成人 | 欧美精品一级视频 | 日韩精品一区二区三区免费观看 | 一区二区三区在线视频观看58 | 中文字幕视频 | 欧美巨乳网 | 亚洲乱亚洲乱妇 | 韩日精品在线 | 亚洲成人av电影在线 | 日韩在线看片 | 日韩欧美成人网 | 亚洲永久精品在线 | 97自拍超碰 | 黄视频色网站 | 国产精品久久久久久久婷婷 | 国产伦精品一区二区三区高清 | 免费福利片2019潦草影视午夜 | 最近2019好看的中文字幕免费 | 九九国产视频 | 国产视频高清 | 久久久午夜精品理论片中文字幕 | 久久久www | 日韩欧美一区二区三区在线 | 国产只有精品 | 日本精品视频一区 | 国产成人在线免费观看 | 国内精品久久久久久久影视简单 | 欧美精品中文字幕亚洲专区 | 亚洲国产精品人久久电影 | 日韩精品免费 | 午夜久久久久久久久久久 | 国产一级二级在线播放 | 精品美女在线视频 | 四虎成人精品在永久免费 | 亚洲国产精品成人va在线观看 | 91精品蜜桃 | 欧美激情另类文学 | 黄色三级网站在线观看 | 日韩精品免费一区二区在线观看 | 成人aaa毛片| 天天草天天干天天 | 色五丁香| 99c视频在线| 粉嫩av一区二区三区四区 | 色a资源在线| 成人资源站 | www178ccom视频在线 | 国产精品系列在线 | 久久久久久久久久久久久国产精品 | 国产探花视频在线播放 | 欧美日韩18 | 亚洲综合视频在线播放 | 国产一级在线播放 | 激情av一区二区 | 久久综合九色99 | 夜添久久精品亚洲国产精品 | 成人在线观看影院 | 免费高清在线观看成人 | 欧美少妇xxx| 91看片一区二区三区 | 国产操在线 | av官网| 国产一区在线看 | 综合久久综合久久 | 欧美做受高潮1 | 日本一区二区不卡高清 | 欧美成年性| 最近最新中文字幕视频 | 色综合天天色 | 最近2019中文免费高清视频观看www99 | 五月婷婷狠狠 | 久久999久久 | 国产精品精品国产色婷婷 | 亚洲高清网站 | www.人人草 | 国产精品岛国久久久久久久久红粉 | 日韩精品欧美专区 | 四虎影视成人永久免费观看视频 | 日韩av进入 | 蜜臀久久99精品久久久无需会员 | 欧美人交a欧美精品 | 久久免费精品国产 | 最近日本韩国中文字幕 | 国产123av| 欧美日韩一区二区三区免费视频 | 日韩免费一区二区在线观看 | 亚洲欧洲一区二区在线观看 | 最新一区二区三区 | 99视频国产精品 | 人成电影网| 亚洲国产av精品毛片鲁大师 | 一区二区欧美日韩 | 岛国av在线不卡 | 综合在线亚洲 | 成人播放器 | 久久国产亚洲精品 | 日韩无在线 | 色婷婷激情四射 | 国产亚洲精品久久久久久网站 | a级国产乱理伦片在线播放 久久久久国产精品一区 | 一级做a爱片性色毛片www | 中文字幕中文字幕中文字幕 | 97超碰人人模人人人爽人人爱 | 国产精品永久在线 | 色多多污污| 日韩在线精品一区 | 色噜噜色噜噜 | 综合久久久 | 极品久久久久 | 国产资源精品在线观看 | 欧美精品做受xxx性少妇 | 五月天久久综合网 | 亚洲aⅴ在线 | 九九久久精品视频 | 成年人在线观看免费视频 | 久久激情日本aⅴ | a国产精品| 亚洲电影久久久 | 91九色精品女同系列 | 久久综合久久综合这里只有精品 | 最新精品视频在线 | 日韩精品一区二区三区免费观看 | 久久久久国产免费免费 | 欧美污网站| 日韩久久久| 亚洲天天在线日亚洲洲精 | 免费aa大片 | 精品在线免费视频 | 91成人免费看片 | 超碰国产在线观看 | 久草在在线 | 久久久久久99精品 | 人人干人人添 | 成人av日韩 | 免费黄a大片 | 国产视频一区二区在线播放 | 亚洲精品国产精品久久99热 | 超薄丝袜一二三区 | 久久久免费视频播放 | 国产色网 | 国产在线最新 | 日本久久中文字幕 | 亚洲va男人天堂 | 91片在线观看 | a在线观看视频 | 99精品色| 欧美 日韩 视频 | 亚洲人人精品 | 亚洲一区日韩在线 | 国产毛片久久久 | 91在线免费观看国产 | 国产精品免费久久久久久久久久中文 | 夜夜骑天天操 | 国内精品久久久精品电影院 | 亚洲无吗视频在线 | 99久久久久久久 | 国产精品色在线 | 亚洲精品在线国产 | 97视频在线看 | 一区二区三区中文字幕在线观看 | 亚洲永久国产精品 | 久久久亚洲成人 | 久久精品人人做人人综合老师 | 三级a毛片| 久久精品国产免费看久久精品 | 日韩在线播放av | 91免费的视频在线播放 | 美女国内精品自产拍在线播放 | 四虎伊人 | 91精品国自产拍天天拍 | 91电影福利 | 日韩免费三级 | 狠狠色丁香婷婷综合 | 麻豆久久久 | av在线播放中文字幕 | 视频在线观看亚洲 | 亚洲资源在线网 | 日韩欧美精品在线 | 91丨九色丨首页 | 狠狠操夜夜 | 黄色免费观看网址 | 在线观看日韩国产 | 色94色欧美 | 亚洲成人频道 | 亚洲午夜久久久久久久久 | 国产高清在线精品 | 国产视频久 | av黄网站 | 五月婷色 | 国产成人久久 | 成人免费在线观看入口 | 午夜国产福利在线观看 | 国产精品久久久久久久久久久久午夜 | 久久视频这里有精品 | 欧美 激情 国产 91 在线 | 国产成人一区二区啪在线观看 | 久草网首页 | 91一区啪爱嗯打偷拍欧美 | 亚洲精品一区二区三区在线观看 | 91在线视频观看 | 亚洲伊人色 | 丁香花在线观看免费完整版视频 | 天天躁日日 | 国产福利一区二区三区在线观看 | 日韩中文字幕a | 日本mv大片欧洲mv大片 | 日韩电影在线观看一区二区 | 欧美精品一区在线 | 日本中文字幕在线看 | 亚洲精品在线观看视频 | 日p视频| 麻豆视频在线免费观看 | 国产色女人 | 在线午夜av| 色a资源在线 | 中文字幕在线观看完整版电影 | 色婷五月天 | 亚洲国产精彩中文乱码av | 欧美亚洲成人xxx | 欧美日本三级 | 欧美 日韩 国产 中文字幕 | 999在线视频| 91亚洲国产成人 | 国产精品成人久久 | 日韩精品免费一区二区三区 | 国产日韩av在线 | 亚洲涩涩色 | 四虎影视www | 午夜精品一区二区三区可下载 | 天堂在线一区二区 | 人人澡人人添人人爽一区二区 | 日韩伦理一区二区三区av在线 | 国产精品久久综合 | 五月婷婷开心中文字幕 | 91香蕉视频 | 国产精品麻豆果冻传媒在线播放 | 国产免费高清 | 天天插天天狠 | 一区三区在线欧 | 深夜免费小视频 | 一区二区三区动漫 | 日韩二区在线播放 | 国产精品观看在线亚洲人成网 | 九九久久影视 | 成人一区电影 | 成人三级av| 又长又大又黑又粗欧美 | 日韩黄色免费看 | 欧美日高清视频 | 国产伦理久久精品久久久久_ | 久久久久激情电影 | 91精品国产91久久久久 | 在线视频黄 | av大片免费看 | 黄色免费在线视频 | 91日韩精品 | 有码视频在线观看 | 天天看天天干天天操 | 美女免费av | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 中文视频一区二区 | 国内精品久久久久影院优 | 91在线资源| 亚洲经典精品 | 日本久久免费视频 | 18做爰免费视频网站 | 天天干天天操天天射 | 天天操天天干天天玩 | 最近最新最好看中文视频 | 日韩精品中文字幕在线观看 | 国产视频精品在线 | 日韩av不卡在线 | 在线中文字母电影观看 | 麻豆影视在线播放 | 日韩在线观看视频免费 | 黄色一级大片在线免费看产 | 中文字幕久久精品一区 | 久久综合狠狠 | 久久久亚洲麻豆日韩精品一区三区 | 日批视频在线播放 | 国产91影视 | 91精品国产乱码 | 国产视频一区二区在线观看 | 国产成人在线综合 | 91污在线 | 久久伦理网 | 亚洲人成人99网站 | 欧美 日韩 性 | 中文字幕资源网在线观看 | 中日韩三级视频 | 精品亚洲欧美一区 | 中文字幕成人网 | 欧美日韩破处 | 久久9999久久免费精品国产 | 中文字幕色在线 | 美女网站视频免费黄 | 亚洲精品在线观看的 | 99爱国产精品 | 欧美精品久久久久 | 国产 亚洲 欧美 在线 | 欧美视频一区二 | 一区二区中文字幕在线观看 | 久久99视频精品 | 免费在线观看一区二区三区 | 人人超碰97 | 超碰在线最新地址 | 欧美日韩一二三四区 | 久草在线一免费新视频 | 国产成年人av | 国产夫妻性生活自拍 | 插婷婷| 亚洲成人网av | 91视频在线免费看 | 深夜视频久久 | 久草精品视频在线看网站免费 | 激情久久久久久久久久久久久久久久 | 国产精品视频地址 | 日韩电影在线观看一区 | 日日夜夜国产 | 天天鲁天天干天天射 | 国产精品第一页在线观看 | 国产淫片 | 午夜精品久久久久久久久久久久 | 久久免费在线视频 | 黄色aa久久 | 欧美在线一二 | 久久99电影| 激情五月看片 | 国产精品久久久久久久免费大片 | 成人超碰97| 一区二区三区手机在线观看 | 黄影院| 中文字幕视频观看 | 高清在线观看av | 青青草在久久免费久久免费 | 国产精品日韩久久久久 | 日韩精品视频免费专区在线播放 | 六月婷婷网 | 国产精品久久久网站 | 国产精品一区二区中文字幕 | 天天干视频在线 | 国产成人一区二区三区影院在线 | 国内精品视频一区二区三区八戒 | 亚洲免费观看视频 | 国产成人精品亚洲日本在线观看 | 色94色欧美 | 久久久91精品国产一区二区精品 | 精品一区久久 | 999视频精品 | 亚洲精品国产精品国自产 | 特级xxxxx欧美 | 人人干狠狠操 | 在线免费观看视频 | 久久深夜福利免费观看 | 午夜精品一区二区三区免费视频 | www欧美色| 国产精品高潮在线观看 | 久久久精品在线观看 | 国产日韩视频在线播放 | 看黄色91| 97碰碰视频 | 亚洲高清视频在线播放 | 五月天视频网 | 国产又粗又猛又黄又爽 | 日韩动态视频 | 91免费版在线观看 | 久久精品91视频 | 色九九影院 | 最新av在线播放 | 婷婷五月色综合 | 国产人在线成免费视频 | 免费性网站| 国产免费高清 | 91中文字幕网 | 亚洲国产成人久久综合 | 色综合久久久网 | a黄色片在线观看 | 91麻豆国产福利在线观看 | 婷婷九月激情 | 免费成人av在线看 | 午夜精品久久久久久中宇69 | 97色涩| 久久一区二区三区日韩 | 国产精品一区二区在线看 | 国产一级片播放 | 久久久av免费| 婷婷丁香在线 | 天堂久色| 色网站免费在线观看 | 黄色成人av网址 | 亚洲精品乱码久久久久久 | 天天操天天射天天爱 | 日韩av成人在线 | www.福利视频 | 天天操操操操操操 | 久久视频网 | 日韩成人在线免费观看 | 国产尤物视频在线 | 久久激情久久 | 日韩91精品 | 最新影院 | 国产丝袜制服在线 | 91精品色 | 色综合久久中文综合久久牛 | av在线不卡观看 | 99久久99热这里只有精品 | 日韩国产精品毛片 | 欧美性久久久 | 欧美精品视 | 国产精品高清免费在线观看 | japanese黑人亚洲人4k | 夜夜躁狠狠躁日日躁 | 国产精品久久二区 | 精品一区二区在线看 | 日韩电影在线观看一区二区 | 免费a级毛片在线看 | www国产亚洲精品 | 国产夫妻性生活自拍 | av在线免费播放 | 日日日操操 | 日本精品久久久久影院 | 中文亚洲欧美日韩 | 91精品秘密在线观看 | 操操操日日日干干干 | 超碰99在线| 亚洲国产精品500在线观看 | 国产成人91| 国产精品 欧美 日韩 | 国产精品1024 | 国产视频一区二区三区在线 | 亚洲精品三级 | 激情视频免费在线观看 | a视频在线观看 | 国产精品九九久久久久久久 | 夜夜躁日日躁狠狠久久av | 免费在线观看不卡av | 视频在线观看99 | 久久精品www人人爽人人 | 久久综合色一综合色88 | 四虎影视成人永久免费观看视频 | 国产 字幕 制服 中文 在线 | 中文字幕在线观看三区 | 久久国产精品一二三区 | 亚洲蜜桃在线 | 在线视频免费观看 | 亚洲欧洲在线视频 | 国内外成人免费在线视频 | 久久视频中文字幕 | 国产99久久久久久免费看 | 在线有码中文字幕 | bbb搡bbb爽爽爽 | 天天曰视频 | 久久久精品日本 | 91精品国产自产91精品 | 欧美日韩在线观看一区 | 久久伊人免费视频 | 一区二区三区四区在线免费观看 | 欧美精品久久久 | 国产日韩在线一区 | 97视频免费播放 | 久久久久久久久久毛片 | 亚洲高清在线视频 | 免费在线观看午夜视频 | 97在线资源 | 成人毛片一区二区三区 | 国产精品片 | 亚洲狠狠婷婷 | 久久久久免费精品 | 婷婷综合av | 欧美性精品 | 亚洲一级片av | www.91av在线 | 亚洲色图22p | av超碰免费在线 | av免费电影在线 | 久久久高清| 亚洲综合情 | 美女黄频 | 天天透天天插 | 中文在线免费观看 | 日韩电影中文,亚洲精品乱码 |