国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

【129期】看完這篇,再也不怕面試被問HashMap了~

共 763字,需瀏覽 2分鐘

 ·

2021-02-02 00:19

程序員的成長之路
互聯網/程序員/技術/資料共享?
關注


閱讀本文大概需要 16?分鐘。

作者:超大只烏龜
https://segmentfault.com/a/1190000022184751

總所周知 HashMap 是面試中經常問到的一個知識點,也是判斷一個候選人基礎是否扎實的標準之一,因為通過?HashMap?可以引出很多知識點,比如數據結構(數組、鏈表、紅黑樹)、equals 和 hashcode?方法。


除此之外還可以引出線程安全的問題,HashMap?是我在初學階段學到的設計的最為巧妙的集合,里面有很多細節(jié)以及優(yōu)化技巧都值得我們深入學習,話不多說先看看相關的面試題:

? ?默認大小、負載因子以及擴容倍數是多少

? ?底層數據結構

? ?如何處理 hash 沖突的

? ?如何計算一個 key 的 hash 值

? ?數組長度為什么是 2 的冪次方

? ?擴容、查找過程

如果上面的都能回答出來的話你就不需要看這篇文章了,那么開始進入正文。

數據結構

? ?在 JDK1.8 中,HashMap 是由數組+鏈表+紅黑樹構成

? ?當一個值中要存儲到 HashMap 中的時候會根據 Key 的值來計算出他的 hash,通過 hash 值來確認存放到數組中的位置,如果發(fā)生 hash 沖突就以鏈表的形式存儲,當鏈表過長的話,HashMap 會把這個鏈表轉換成紅黑樹來存儲。

在看源碼之前我們需要先看看一些基本屬性

//默認初始容量為16??
static?final?int?DEFAULT_INITIAL_CAPACITY?=?1?<4;??
//默認負載因子為0.75??
static?final?float?DEFAULT_LOAD_FACTOR?=?0.75f;??
//Hash數組(在resize()中初始化)??
transient?Node[]?table;??
//元素個數??
transient?int?size;??
//容量閾值(元素個數超過該值會自動擴容)??
int?threshold;

table 數組里面存放的是 Node 對象,Node 是 HashMap 的一個內部類,用來表示一個 key-value,源碼如下:

static?class?Node<K,V>?implements?Map.Entry<K,V>?{??
????final?int?hash;??
????final?K?key;??
????V?value;??
????Node?next;??

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

????public?final?K?getKey()????????{?return?key;?}??
????public?final?V?getValue()??????{?return?value;?}??
????public?final?String?toString()?{?return?key?+?"="?+?value;?}??
????public?final?int?hashCode()?{??
????????return?Objects.hashCode(key)?^?Objects.hashCode(value);//^表示相同返回0,不同返回1??
????????//Objects.hashCode(o)————>return?o?!=?null???o.hashCode()?:?0;??
????}??

????public?final?V?setValue(V?newValue)?{??
????????V?oldValue?=?value;??
????????value?=?newValue;??
????????return?oldValue;??
????}??

????public?final?boolean?equals(Object?o)?{??
????????if?(o?==?this)??
????????????return?true;??
????????if?(o?instanceof?Map.Entry)?{??
????????????Map.Entry?e?=?(Map.Entry)o;??
????????????//Objects.equals(1,b)————>?return?(a?==?b)?||?(a?!=?null?&&?a.equals(b));??
????????????if?(Objects.equals(key,?e.getKey())?&&?Objects.equals(value,?e.getValue()))??
????????????????return?true;??
????????}??
????????return?false;??
????}??
}

總結:

? ?默認初始容量為 16,默認負載因子為 0.75

? ?threshold = 數組長度 * loadFactor,當元素個數超過threshold(容量閾值)時,HashMap 會進行擴容操作

? ?table 數組中存放指向鏈表的引用

這里需要注意的一點是 table 數組并不是在構造方法里面初始化的,它是在 resize(擴容)方法里進行初始化的。

table 數組長度永遠為 2 的冪次方

總所周知,HashMap 數組長度永遠為 2 的冪次方(指的是 table 數組的大小),那你有想過為什么嗎?

首先我們需要知道 HashMap 是通過一個名為 tableSizeFor 的方法來確保 HashMap 數組長度永遠為2的冪次方的,源碼如下:

/*找到大于或等于?cap?的最小2的冪,用來做容量閾值*/??
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;??
}

tableSizeFor 的功能(不考慮大于最大容量的情況)是返回大于等于輸入參數且最近的 2 的整數次冪的數。比如 10,則返回 16。

該算法讓最高位的 1 后面的位全變?yōu)?1。最后再讓結果 n+1,即得到了 2 的整數次冪的值了。

讓 cap-1 再賦值給 n 的目的是另找到的目標值大于或等于原值。例如二進制 1000,十進制數值為 8。如果不對它減1而直接操作,將得到答案 10000,即 16。顯然不是結果。減 1 后二進制為 111,再進行操作則會得到原來的數值 1000,即 8。通過一系列位運算大大提高效率。

那在什么地方會用到 tableSizeFor 方法呢?

答案就是在構造方法里面調用該方法來設置 threshold,也就是容量閾值。

這里你可能又會有一個疑問:為什么要設置為 threshold 呢?

因為在擴容方法里第一次初始化 table 數組時會將 threshold 設置數組的長度,后續(xù)在講擴容方法時再介紹。

/*傳入初始容量和負載因子*/??
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;??
????this.threshold?=?tableSizeFor(initialCapacity);??
}

那么為什么要把數組長度設計為 2 的冪次方呢?

我個人覺得這樣設計有以下幾個好處:

1. 當數組長度為 2 的冪次方時,可以使用位運算來計算元素在數組中的下標

HashMap 是通過 index=hash&(table.length-1) 這條公式來計算元素在 table 數組中存放的下標,就是把元素的 hash 值和數組長度減1的值做一個與運算,即可求出該元素在數組中的下標,這條公式其實等價于 hash%length,也就是對數組長度求模取余,只不過只有當數組長度為 2 的冪次方時,hash&(length-1) 才等價于 hash%length,使用位運算可以提高效率。

2. 增加 hash 值的隨機性,減少 hash 沖突

如果 length 為 2 的冪次方,則 length-1 轉化為二進制必定是 11111……的形式,這樣的話可以使所有位置都能和元素 hash 值做與運算,如果是如果 length 不是 2 的次冪,比如 length 為 15,則 length-1 為 14,對應的二進制為 1110,在和 hash 做與運算時,最后一位永遠都為 0 ,浪費空間。

擴容

HashMap 每次擴容都是建立一個新的 table 數組,長度和容量閾值都變?yōu)樵瓉淼膬杀?,然后把原數組元素重新映射到新數組上,具體步驟如下:

1. 首先會判斷 table 數組長度,如果大于 0 說明已被初始化過,那么按當前 table 數組長度的 2 倍進行擴容,閾值也變?yōu)樵瓉淼?2 倍

2. 若 table 數組未被初始化過,且 threshold(閾值)大于 0 說明調用了 HashMap(initialCapacity, loadFactor) 構造方法,那么就把數組大小設為 threshold

3. 若 table 數組未被初始化,且 threshold 為 0 說明調用 HashMap() 構造方法,那么就把數組大小設為 16,threshold 設為 16*0.75

4. 接著需要判斷如果不是第一次初始化,那么擴容之后,要重新計算鍵值對的位置,并把它們移動到合適的位置上去,如果節(jié)點是紅黑樹類型的話則需要進行紅黑樹的拆分。

這里有一個需要注意的點就是在 JDK1.8 HashMap 擴容階段重新映射元素時不需要像 1.7 版本那樣重新去一個個計算元素的 hash 值,而是通過 hash & oldCap 的值來判斷,若為 0 則索引位置不變,不為 0 則新索引=原索引+舊數組長度,為什么呢?具體原因如下:

因為我們使用的是 2 次冪的擴展(指長度擴為原來 2 倍),所以,元素的位置要么是在原位置,要么是在原位置再移動 2 次冪的位置。因此,我們在擴充 HashMap 的時候,不需要像 JDK1.7 的實現那樣重新計算 hash,只需要看看原來的 hash 值新增的那個 bit 是 1 還是 0 就好了,是 0 的話索引沒變,是 1 的話索引變成“原索引 +oldCap

這點其實也可以看做長度為 2 的冪次方的一個好處,也是 HashMap 1.7 和 1.8 之間的一個區(qū)別,具體源碼如下:

/*擴容*/??
final?Node[]?resize()?{??
????Node[]?oldTab?=?table;??
????int?oldCap?=?(oldTab?==?null)???0?:?oldTab.length;??
????int?oldThr?=?threshold;??
????int?newCap,?newThr?=?0;??

????//1、若oldCap>0?說明hash數組table已被初始化??
????if?(oldCap?>?0)?{??
????????if?(oldCap?>=?MAXIMUM_CAPACITY)?{??
????????????threshold?=?Integer.MAX_VALUE;??
????????????return?oldTab;??
????????}//按當前table數組長度的2倍進行擴容,閾值也變?yōu)樵瓉淼?倍??
????????else?if?((newCap?=?oldCap?<1)?=?DEFAULT_INITIAL_CAPACITY)??
????????????newThr?=?oldThr?<1;??
????}//2、若數組未被初始化,而threshold>0說明調用了HashMap(initialCapacity)和HashMap(initialCapacity,?loadFactor)構造器??
????else?if?(oldThr?>?0)??
????????newCap?=?oldThr;//新容量設為數組閾值??
????else?{?//3、若table數組未被初始化,且threshold為0說明調用HashMap()構造方法??
????????newCap?=?DEFAULT_INITIAL_CAPACITY;//默認為16??
????????newThr?=?(int)(DEFAULT_LOAD_FACTOR?*?DEFAULT_INITIAL_CAPACITY);//16*0.75??
????}??

????//若計算過程中,閾值溢出歸零,則按閾值公式重新計算??
????if?(newThr?==?0)?{??
????????float?ft?=?(float)newCap?*?loadFactor;??
????????newThr?=?(newCap?float)MAXIMUM_CAPACITY????
??????????????????(int)ft?:?Integer.MAX_VALUE);??
????}??
????threshold?=?newThr;??
????//創(chuàng)建新的hash數組,hash數組的初始化也是在這里完成的??
????Node[]?newTab?=?(Node[])new?Node[newCap];??
????table?=?newTab;??
????//如果舊的hash數組不為空,則遍歷舊數組并映射到新的hash數組??
????if?(oldTab?!=?null)?{??
????????for?(int?j?=?0;?j?????????????Node?e;??
????????????if?((e?=?oldTab[j])?!=?null)?{??
????????????????oldTab[j]?=?null;//GC??
????????????????if?(e.next?==?null)//如果只鏈接一個節(jié)點,重新計算并放入新數組??
????????????????????newTab[e.hash?&?(newCap?-?1)]?=?e;??
????????????????//若是紅黑樹,則需要進行拆分??
????????????????else?if?(e?instanceof?TreeNode)??
????????????????????((TreeNode)e).split(this,?newTab,?j,?oldCap);??
????????????????else?{??
????????????????????//rehash————>重新映射到新數組??
????????????????????Node?loHead?=?null,?loTail?=?null;??
????????????????????Node?hiHead?=?null,?hiTail?=?null;??
????????????????????Node?next;??
????????????????????do?{??
????????????????????????next?=?e.next;??
????????????????????????/*注意這里使用的是:e.hash & oldCap,若為0則索引位置不變,不為0則新索引=原索引+舊數組長度*/??
????????????????????????if?((e.hash?&?oldCap)?==?0)?{??
????????????????????????????if?(loTail?==?null)??
????????????????????????????????loHead?=?e;??
????????????????????????????else??
????????????????????????????????loTail.next?=?e;??
????????????????????????????loTail?=?e;??
????????????????????????}??
????????????????????????else?{??
????????????????????????????if?(hiTail?==?null)??
????????????????????????????????hiHead?=?e;??
????????????????????????????else??
????????????????????????????????hiTail.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;??
}

在擴容方法里面還涉及到有關紅黑樹的幾個知識點:

鏈表樹化

指的就是把鏈表轉換成紅黑樹,樹化需要滿足以下兩個條件:

  • 鏈表長度大于等于 8

  • table 數組長度大于等于 64

為什么 table 數組容量大于等于 64 才樹化?

因為當 table 數組容量比較小時,鍵值對節(jié)點 hash 的碰撞率可能會比較高,進而導致鏈表長度較長。這個時候應該優(yōu)先擴容,而不是立馬樹化。

紅黑樹拆分

拆分就是指擴容后對元素重新映射時,紅黑樹可能會被拆分成兩條鏈表。

由于篇幅有限,有關紅黑樹這里就不展開了。

查找

HashMap 的查找是非??斓模檎乙粋€元素首先得知道 key 的 hash 值,在 HashMap 中并不是直接通過 key 的 hashcode 方法獲取哈希值,而是通過內部自定義的 hash 方法計算哈希值,我們來看看其實現:

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

(h = key.hashCode()) ^ (h >>> 16) 是為了讓高位數據與低位數據進行異或,變相的讓高位數據參與到計算中,int 有 32 位,右移 16 位就能讓低 16 位和高 16 位進行異或,也是為了增加 hash 值的隨機性。

知道如何計算 hash 值后我們來看看 get 方法

public?V?get(Object?key)?{??
????Node?e;??
????return?(e?=?getNode(hash(key),?key))?==?null???null?:?e.value;//hash(key)不等于key.hashCode??
}??

final?Node?getNode(int?hash,?Object?key)?{??
????Node[]?tab;?//指向hash數組??
????Node?first,?e;?//first指向hash數組鏈接的第一個節(jié)點,e指向下一個節(jié)點??
????int?n;//hash數組長度??
????K?k;??
????/*(n?-?1)?&?hash?————>根據hash值計算出在數組中的索引index(相當于對數組長度取模,這里用位運算進行了優(yōu)化)*/??
????if?((tab?=?table)?!=?null?&&?(n?=?tab.length)?>?0?&&?(first?=?tab[(n?-?1)?&?hash])?!=?null)?{??
????????//基本類型用==比較,其它用euqals比較??
????????if?(first.hash?==?hash?&&?((k?=?first.key)?==?key?||?(key?!=?null?&&?key.equals(k))))??
????????????return?first;??
????????if?((e?=?first.next)?!=?null)?{??
????????????//如果first是TreeNode類型,則調用紅黑樹查找方法??
????????????if?(first?instanceof?TreeNode)??
????????????????return?((TreeNode)first).getTreeNode(hash,?key);??
????????????do?{//向后遍歷??
????????????????if?(e.hash?==?hash?&&?((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))??
????????????????????return?e;??
????????????}?while?((e?=?e.next)?!=?null);??
????????}??
????}??
????return?null;??
}`

這里要注意的一點就是在 HashMap 中用 (n - 1) & hash 計算 key 所對應的索引 index(相當于對數組長度取模,這里用位運算進行了優(yōu)化),這點在上面已經說過了,就不再廢話了。

插入

我們先來看看插入元素的步驟:

1. 當 table 數組為空時,通過擴容的方式初始化 table

2. 通過計算鍵的 hash 值求出下標后,若該位置上沒有元素(沒有發(fā)生 hash 沖突),則新建 Node 節(jié)點插入

3. 若發(fā)生了 hash 沖突,遍歷鏈表查找要插入的 key 是否已經存在,存在的話根據條件判斷是否用新值替換舊值

4. 如果不存在,則將元素插入鏈表尾部,并根據鏈表長度決定是否將鏈表轉為紅黑樹

5. 判斷鍵值對數量是否大于閾值,大于的話則進行擴容操作

先看完上面的流程,再來看源碼會簡單很多,源碼如下:

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

final?V?putVal(int?hash,?K?key,?V?value,?boolean?onlyIfAbsent,boolean?evict)?{??
????Node[]?tab;//指向hash數組??
????Node?p;//初始化為table中第一個節(jié)點??
????int?n,?i;//n為數組長度,i為索引??

????//tab被延遲到插入新數據時再進行初始化??
????if?((tab?=?table)?==?null?||?(n?=?tab.length)?==?0)??
????????n?=?(tab?=?resize()).length;??
????//如果數組中不包含Node引用,則新建Node節(jié)點存入數組中即可??
????if?((p?=?tab[i?=?(n?-?1)?&?hash])?==?null)??
????????tab[i]?=?newNode(hash,?key,?value,?null);//new?Node<>(hash,?key,?value,?next)??
????else?{??
????????Node?e;?//如果要插入的key-value已存在,用e指向該節(jié)點??
????????K?k;??
????????//如果第一個節(jié)點就是要插入的key-value,則讓e指向第一個節(jié)點(p在這里指向第一個節(jié)點)??
????????if?(p.hash?==?hash?&&?((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))??
????????????e?=?p;??
????????//如果p是TreeNode類型,則調用紅黑樹的插入操作(注意:TreeNode是Node的子類)??
????????else?if?(p?instanceof?TreeNode)??
????????????e?=?((TreeNode)p).putTreeVal(this,?tab,?hash,?key,?value);??
????????else?{??
????????????//對鏈表進行遍歷,并用binCount統計鏈表長度??
????????????for?(int?binCount?=?0;?;?++binCount)?{??
????????????????//如果鏈表中不包含要插入的key-value,則將其插入到鏈表尾部??
????????????????if?((e?=?p.next)?==?null)?{??
????????????????????p.next?=?newNode(hash,?key,?value,?null);??
????????????????????//如果鏈表長度大于或等于樹化閾值,則進行樹化操作??
????????????????????if?(binCount?>=?TREEIFY_THRESHOLD?-?1)??
????????????????????????treeifyBin(tab,?hash);??
????????????????????break;??
????????????????}??
????????????????//如果要插入的key-value已存在則終止遍歷,否則向后遍歷??
????????????????if?(e.hash?==?hash?&&?((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))??
????????????????????break;??
????????????????p?=?e;??
????????????}??
????????}??
????????//如果e不為null說明要插入的key-value已存在??
????????if?(e?!=?null)?{??
????????????V?oldValue?=?e.value;??
????????????//根據傳入的onlyIfAbsent判斷是否要更新舊值??
????????????if?(!onlyIfAbsent?||?oldValue?==?null)??
????????????????e.value?=?value;??
????????????afterNodeAccess(e);??
????????????return?oldValue;??
????????}??
????}??
????++modCount;??
????//鍵值對數量超過閾值時,則進行擴容??
????if?(++size?>?threshold)??
????????resize();??
????afterNodeInsertion(evict);//也是空函數?回調?不知道干嘛的??
????return?null;??
}

從源碼也可以看出 table 數組是在第一次調用 put 方法后才進行初始化的。

刪除

HashMap 的刪除操作并不復雜,僅需三個步驟即可完成。

1. 定位桶位置

2. 遍歷鏈表找到相等的節(jié)點

3. 第三步刪除節(jié)點

public?V?remove(Object?key)?{??
????Node?e;??
????return?(e?=?removeNode(hash(key),?key,?null,?false,?true))?==?null???null?:?e.value;??
}??

final?Node?removeNode(int?hash,?Object?key,?Object?value,boolean?matchValue,?boolean?movable)?{??
????Node[]?tab;??
????Node?p;??
????int?n,?index;??
????//1、定位元素桶位置??
????if?((tab?=?table)?!=?null?&&?(n?=?tab.length)?>?0?&&?(p?=?tab[index?=?(n?-?1)?&?hash])?!=?null)?{??
????????Node?node?=?null,?e;??
????????K?k;??
????????V?v;??
????????//?如果鍵的值與鏈表第一個節(jié)點相等,則將?node?指向該節(jié)點??
????????if?(p.hash?==?hash?&&?((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))??
????????????node?=?p;??
????????else?if?((e?=?p.next)?!=?null)?{??
????????????//?如果是?TreeNode?類型,調用紅黑樹的查找邏輯定位待刪除節(jié)點??
????????????if?(p?instanceof?TreeNode)??
????????????????node?=?((TreeNode)p).getTreeNode(hash,?key);??
????????????else?{??
????????????????//?2、遍歷鏈表,找到待刪除節(jié)點??
????????????????do?{??
????????????????????if?(e.hash?==?hash?&&?((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))?{??
????????????????????????node?=?e;??
????????????????????????break;??
????????????????????}??
????????????????????p?=?e;??
????????????????}?while?((e?=?e.next)?!=?null);??
????????????}??
????????}??
????????//?3、刪除節(jié)點,并修復鏈表或紅黑樹??
????????if?(node?!=?null?&&?(!matchValue?||?(v?=?node.value)?==?value?||?(value?!=?null?&&?value.equals(v))))?{??
????????????if?(node?instanceof?TreeNode)??
????????????????((TreeNode)node).removeTreeNode(this,?tab,?movable);??
????????????else?if?(node?==?p)??
????????????????tab[index]?=?node.next;??
????????????else??
????????????????p.next?=?node.next;??
????????????++modCount;??
????????????--size;??
????????????afterNodeRemoval(node);??
????????????return?node;??
????????}??
????}??
????return?null;??
}

注意:刪除節(jié)點后可能破壞了紅黑樹的平衡性質,removeTreeNode 方法會對紅黑樹進行變色、旋轉等操作來保持紅黑樹的平衡結構,這部分比較復雜。

遍歷

在工作中 HashMap 的遍歷操作也是非常常用的,也許有很多小伙伴喜歡用 for-each 來遍歷,但是你知道其中有哪些坑嗎?

看下面的例子,當我們在遍歷 HashMap 的時候,若使用 remove 方法刪除元素時會拋出 ConcurrentModificationException 異常

Map?map?=?new?HashMap<>();??
map.put("1",?1);??
map.put("2",?2);??
map.put("3",?3);??
for?(String?s?:?map.keySet())?{??
????if?(s.equals("2"))??
????????map.remove("2");??
}

這就是常說的 fail-fast(快速失敗)機制,這個就需要從一個變量說起

transient?int?modCount;

在 HashMap 中有一個名為 modCount 的變量,它用來表示集合被修改的次數,修改指的是插入元素或刪除元素,可以回去看看上面插入刪除的源碼,在最后都會對 modCount 進行自增。

當我們在遍歷 HashMap 時,每次遍歷下一個元素前都會對 modCount 進行判斷,若和原來的不一致說明集合結果被修改過了,然后就會拋出異常,這是 Java 集合的一個特性,我們這里以 keySet 為例,看看部分相關源碼:

public?Set?keySet()?{??
????Set?ks?=?keySet;??
????if?(ks?==?null)?{??
????????ks?=?new?KeySet();??
????????keySet?=?ks;??
????}??
????return?ks;??
}??

final?class?KeySet?extends?AbstractSet<K>?{??
????public?final?Iterator?iterator()?????{?return?new?KeyIterator();?}??
????//?省略部分代碼??
}??

final?class?KeyIterator?extends?HashIterator?implements?Iterator<K>?{??
????public?final?K?next()?{?return?nextNode().key;?}??
}??

/*HashMap迭代器基類,子類有KeyIterator、ValueIterator等*/??
abstract?class?HashIterator?{??
????Node?next;????????//下一個節(jié)點??
????Node?current;?????//當前節(jié)點??
????int?expectedModCount;??//修改次數??
????int?index;?????????????//當前索引??
????//無參構造??
????HashIterator()?{??
????????expectedModCount?=?modCount;??
????????Node[]?t?=?table;??
????????current?=?next?=?null;??
????????index?=?0;??
????????//找到第一個不為空的桶的索引??
????????if?(t?!=?null?&&?size?>?0)?{??
????????????do?{}?while?(index?null);??
????????}??
????}??
????//是否有下一個節(jié)點??
????public?final?boolean?hasNext()?{??
????????return?next?!=?null;??
????}??
????//返回下一個節(jié)點??
????final?Node?nextNode()?{??
????????Node[]?t;??
????????Node?e?=?next;??
????????if?(modCount?!=?expectedModCount)??
????????????throw?new?ConcurrentModificationException();//fail-fast??
????????if?(e?==?null)??
????????????throw?new?NoSuchElementException();??
????????//當前的鏈表遍歷完了就開始遍歷下一個鏈表??
????????if?((next?=?(current?=?e).next)?==?null?&&?(t?=?table)?!=?null)?{??
????????????do?{}?while?(index?null);??
????????}??
????????return?e;??
????}??
????//刪除元素??
????public?final?void?remove()?{??
????????Node?p?=?current;??
????????if?(p?==?null)??
????????????throw?new?IllegalStateException();??
????????if?(modCount?!=?expectedModCount)??
????????????throw?new?ConcurrentModificationException();??
????????current?=?null;??
????????K?key?=?p.key;??
????????removeNode(hash(key),?key,?null,?false,?false);//調用外部的removeNode??
????????expectedModCount?=?modCount;??
????}??
}

相關代碼如下,可以看到若 modCount 被修改了則會拋出 ConcurrentModificationException 異常。

if?(modCount?!=?expectedModCount)??
????throw?new?ConcurrentModificationException();

那么如何在遍歷時刪除元素呢?

我們可以看看迭代器自帶的 remove 方法,其中最后兩行代碼如下:

`removeNode(hash(key),?key,?null,?false,?false);//調用外部的removeNode??
expectedModCount?=?modCount;
`

意思就是會調用外部 remove 方法刪除元素后,把 modCount 賦值給 expectedModCount,這樣的話兩者一致就不會拋出異常了,所以我們應該這樣寫:

Map?map?=?new?HashMap<>();??
map.put("1",?1);??
map.put("2",?2);??
map.put("3",?3);??
Iterator?iterator?=?map.keySet().iterator();??
while?(iterator.hasNext()){??
????if?(iterator.next().equals("2"))??
????????iterator.remove();??
}

這里還有一個知識點就是在遍歷 HashMap 時,我們會發(fā)現遍歷的順序和插入的順序不一致,這是為什么?

在 HashIterator 源碼里面可以看出,它是先從桶數組中找到包含鏈表節(jié)點引用的桶。然后對這個桶指向的鏈表進行遍歷。遍歷完成后,再繼續(xù)尋找下一個包含鏈表節(jié)點引用的桶,找到繼續(xù)遍歷。找不到,則結束遍歷。這就解釋了為什么遍歷和插入的順序不一致,不懂的同學請看下圖:

equasl 和 hashcode

為什么添加到 HashMap 中的對象需要重寫 equals() 和 hashcode() 方法?

簡單看個例子,這里以 Person 為例:

public?class?Person?{??
????Integer?id;??
????String?name;??

????public?Person(Integer?id,?String?name)?{??
????????this.id?=?id;??
????????this.name?=?name;??
????}??

????@Override??
????public?boolean?equals(Object?obj)?{??
????????if?(obj?==?null)?return?false;??
????????if?(obj?==?this)?return?true;??
????????if?(obj?instanceof?Person)?{??
????????????Person?person?=?(Person)?obj;??
????????????if?(this.id?==?person.id)??
????????????????return?true;??
????????}??
????????return?false;??
????}??

????public?static?void?main(String[]?args)?{??
????????Person?p1?=?new?Person(1,?"aaa");??
????????Person?p2?=?new?Person(1,?"bbb");??
????????HashMap?map?=?new?HashMap<>();??
????????map.put(p1,?"這是p1");??
????????System.out.println(map.get(p2));??
????}??
}

?原生的 equals 方法是使用 == 來比較對象的
?原生的 hashCode 值是根據內存地址換算出來的一個值

Person 類重寫 equals 方法來根據 id 判斷是否相等,當沒有重寫 hashcode 方法時,插入 p1 后便無法用 p2 取出元素,這是因為 p1 和 p2 的哈希值不相等。

HashMap 插入元素時是根據元素的哈希值來確定存放在數組中的位置,因此HashMap 的 key 需要重寫 equals 和 hashcode 方法。

總結

本文描述了 HashMap 的實現原理,并結合源碼做了進一步的分析,后續(xù)有空的話會聊聊有關 HashMap 的線程安全問題,希望本篇文章能幫助到大家,同時也歡迎討論指正,謝謝支持!

推薦閱讀:

【128期】一道搜狗面試題:IO多路復用中select、poll、epoll之間的區(qū)別

【127期】面試官:你說使用過ZooKeeper,那來說說他的基本原理吧

【126期】消息隊列面試連環(huán)炮

5T技術資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內回復「2048」,即可免費獲?。。?/span>

微信掃描二維碼,關注我的公眾號

朕已閱?

瀏覽 46
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 三级黄色免费网站| 国产精品一区二| 特黄色视频| 水多多成人免费A片| 亚洲无人禁区| 安徽妇搡BBBB搡BBBB小说| 中文字幕亚洲在线观看| 91网站免费看| 日皮视频在线| 高清无码免费不卡| 精品91在线视频| 中文字幕一二三| 手机看片福利一区二区| 爱操AV| 中国九九盗摄偷拍偷看| 中文字幕乱码中文字幕电视剧| 91干逼| 亚洲黄色Av| 黄色小说在线看| 久久久综合网| 国产久久精品视频| 亚洲国产精品久久久久婷婷老年 | 亚洲在线第一页| 亚洲第一成人久久网站| 人人摸人人操人人看| 亚洲成年人网| 九九热播精品| 国产无码成人电影| 操逼视频,黄色大全| 77777色婷婷| 成人做爰黄A片免费看直播室动漫| 国产成人高潮毛片| 无码av一区| 黄a无码| 国产高清无码一区| h片无码| 少妇厨房愉情理伦BD在线观| 伊人久久久久久久久久久| 日韩无码高清网站| 日韩AV无码网站| 日韩综合| 五月婷婷色| av无码aV天天aV天天爽| 日韩人妻精品中文字幕免费| 麻豆91视频| 91精品丝袜久久久久久久久久粉嫩 | 亚洲福利一区| 人人射人人爱| 66久久| 青春草在线免费视频| 免费视频99| 亚洲一线视频| 18成人网站在线观看| 精品成人网| 激情五月婷婷网| 99久久精彩视频| 亚洲自拍天堂| 国产精品三级在线| 亚洲欧美精品AAAAAA片| 国产精品可站18| 欧美亚洲成人精品| 天堂精品在线| 亚洲精品国产精品乱码视99 | 99re国产| 青草视频在线| 国精产品秘一区二区-| 超碰av在线| 大香蕉亚洲| 亚洲黄色影院| 乱伦A片| 蜜桃av秘无码一区二区三区| 俺去也视频| 蜜桃传媒一区二区亚洲AV| 欧美日韩A| www.国产视频| 91看片| 国产黃色AAA片| 亚州激情| 欧美精品秘一区二区三区蜜臀| 小處女末发育嫩苞AV| 国产二级片| 爱看福利视频| 男人的天堂2019| 51妺嘿嘿午夜福利| 99精品视频在线播放免费| 国语对白做受欧美| 国产av天堂| 国产对白在线| 18XXX亚洲HD护士JD| 大鸡巴在线| 久久成人精品| AV解说| 黑人操逼| 国产曰韩欧美综合另类在线| 男人天堂手机视频| 人妻少妇偷人精品无码免费| 免费的黄色A片| av色站| 亚洲国产视频在线观看| 日韩AV无码专区亚洲AV紧身裤 | 成人无码区免费AV毛片| 嫩草视频| 大香蕉国产视频| 黄色国产视频| 亚洲成人一区二区在线观看| 佐山爱人妻无码蜜桃| 日韩欧美国产黄色电影| 久草网址| 国产一级特黄大片| 婷婷亚洲精品| 亚洲AV无码专区在线播放中文| 爱逼综合网| 欧美日韩黄色片| 大黄网站在线观看| 日韩精品一区在线观看| 亚洲天堂免费| 天天操天天看| 人妻无码一二三区免费| 美女啪啪网站| 日韩国产综合| 一本一道波多野结衣潮喷视频| 亚洲AV成人一区二区三区不卡 | 免费成人黄色网址| 欧美精品秘一区二区三区蜜臀| 99久久九九| 久久久久亚洲AV成人无码电影| 色999网址| 久久6热| 全部在线A片免费播放| 东北奇淫老老妇| 欧美激情精品| 欧美av| 天天狠狠干| 国产精品自拍小视频| 欧美操逼图片| 先锋影音资源站| 日本一级片在线播放| 欧美精产国品一| 91日韩视频在线| 在线国产激情| 9l视频自拍蝌蚪9l成人| 视频在线观看一区| 国产av黄色| 大香蕉一区二区三区| 亚洲男人综合| 久久久久久一区| 日本三区视频| ChineSe露脸老女人| Japanese在线观看| 色噜噜在线| 欧美激情在线| 天堂精品在线| 成年人黄色视频免费观看| 在线观看欧美黄片| 性爱一级片| 9l视频自拍蝌蚪9l成人蝌蚪| www.国产在线观看| 天天精品| 日韩中文字幕一区二区三区| 国产一区二区不卡视频| 五月天婷婷成人| 欧美激情片| 色五月天婷婷| 这里有精品| 四虎2025在线51| 国产欧美日韩综合精品| 亚洲成人精品AV| 人人澡超碰碰| 色午夜| 人人插人人| 欧美footjob| 又大又粗AV| 天天天天天天天操| 精品久久成人| A片免费网站| 人妻精品一卡二卡| 精品少妇无码视频| 久草在| 日逼欧美| 在线你懂| 五丁香在线观看AV| 久久高潮| 日韩美女免费视频| 国产91在线亚洲| 免费无码高清视频| 在线免费看AV片| 黄色草莓视频| 天天射夜夜操| 婷婷色亚洲| 久草视频免费在线观看| 婷婷成人小说| 日本一级黄色| 中文字幕永久在线观看| 91香蕉视频在线播放| 午夜亚洲国产一区视频网站 | 久久久精品国产| 香蕉婷婷亚洲丁香| 麻豆午夜福利视频| 亚洲黄色视频网站在线观看| 午夜无码人妻AV| 青春草视频| igao在线观看| 人人看人人摸人人草| 操逼视频无码| eeuss| 688AV秘无码一区二区| 91A视频| av怡红院| 在线观看中文字幕| 国产黄色视频在线播放| 先锋影音资源AV| 操逼观看| 久久精品美臀| 大香蕉网站视频| 男人的天堂手机在线| 免费无码成人片在线播放| 丁香六月婷婷久久综合| 性猛交AAAA片免费观看直播| 97人人色| 一级女婬片A片AAAA片| 中文AV第一页| 黑人在线视频| A级黄色电影| www.日韩精品| 高清无码操逼视频| 欧美精品一卡二卡| 在线观看禁无码精品| 欧洲天堂在线视频网站| 九久久| 亚洲无码观看视频| 亚洲无码免费看| 欧美精品操逼| 亚洲激情欧美| 91蝌蚪丨人妻丨丝袜| 中文字幕-区二区三区四区视频中国| 69无码| 91精品少妇| 国产成人精品麻豆| 亚洲无码在线播放| 精品国产一二三| 欧美亚洲日韩在线观看| 久久少妇视频| 欧美精品一区二区少妇免费A片| 亚洲AV毛片成人精品网站| 不卡视频一区二区| 无码视频播放| 国产视频97| 婷婷三级片| 强伦轩一区二区三区四区播放方式| 欧美精品国产动漫| 久久熟女嫩草成人片免费| 久久爆乳一区二区三区| 国产免费成人在线观看| 久久久成人免费视频| 黄片视频网站| 69av视频在线观看| 久久久成人免费视频| 成人无码区免费AV毛片| 日韩av中文字幕在线播放| ww免费视频| 99热精品在线播放| 日韩特黄片| 国产操逼的视频| 日韩AV电影在线观看| A视频免费在线观看| 亚洲无码精品一区| 色噜噜狠狠色综无码久久合欧美| 日韩免费网| 91AV免费观看| 久热中文字幕| 亚洲AV无码乱码国产精品| 在线免费看a| 懂色成人av影院| 国产在线播放av| 成人特级毛片全部免费播放| 国产日本在线视频| 少妇厨房愉情理伦BD在线观看| 无码人妻一区二区三区| 免费AV资源在线观看| 国产女主播在线| 国产精品秘久久久久久久久| 亚洲精品久久久久久久蜜桃| 三级片一区二区| AV天堂影视在线观看| 国产嫩草视频| 高清av无码| 国产女人18毛片水真多18| 毛片黄色视频| 99精品视频在线| 在线观看操逼| 一道本一区二区| 欧美日本一区二区三区| 欧美人妻日韩精品| 色色婷婷五月天| 大地中文资源5页的更新内容| A视频在线| 国产美女操逼网站| 亚洲欧美第一页| 久久人人操| 美女超碰| 偷拍综合网| 自慰一区| 日本成人一区| 色综合五月| 三级片高清无码| 亚洲综合五月天婷婷丁香| 国产精品v欧美精品v日韩| 北条麻妃91| 成人福利免费视频| 亚洲秘无码一区二区三区蜜桃中文| 丰臀肥逼高清视频电影播放| 97人人澡| 天堂一区二区三区| 热九九热| 国产精品秘麻豆果冻传媒潘甜甜丶| 人人操人人网站| 黄色三级在线| 日韩人妻无码网站| 囯产精品一区二区三区线一牛影视1| 天天干天天日天天操| 久草电影在线观看| 人人操人人摸人人干| 免费看黄色片视频| 免费操逼视频网站| 操久在线| 成人电影一区| 精品国产久久久久| 神马午夜精品| 97视频福利| 成人免费视频国产免费麻豆,| 亚洲天堂2014| 好男人WWW一区二区三区| 德美日三级片在线观看| 欧美性爱69| 亚洲不卡视频| 欧美亚洲成人电影| 无码乱伦| 五月婷婷色色色| 国产黄色录像| 开心激情网五月天| 国产激情视频在线| 182在线视频| 波多野结衣性爱视频| 探花极品无套大学生| 精品视频在线免费观看| 欧美日韩一区二区在线| 中文字幕无码在线视频| A片黄色| 成人性生活免费视频| 日韩中字幕无码| 精品人妻一区二区三区-国产精品| 久久青青草在线视频| 色资源在线| 亚洲午夜久久| a免费视频| 精品久久无码| 一级片日韩| 一级黄色免费视频| 激情乱伦视频| 日比视频网站| 国内操B电影| h在线| 91人妻日韩人妻无码专区精品| 日韩在线免费看| 草莓av| 尤物最新网址| 黄色小网站在线观看| 无码人妻日韩精品一区二区三| 狼色视频| 久久精品大香蕉| 狼人香蕉在线视频| 粉嫩av在线| 国产乱伦网| 成人乱码一区二区三区| 久久婷婷婬片A片AAA| 91资源在线| 大香蕉伊人操| 丁香五月网站| 黄色一区二区三区| 免费黄色一级视频| 人妻精品一区二区| a在线观看免费| 国产精品久久久久久久久久久久久久久 | 人妻av在线| 韩国一区二区三区在线观看| 国产97在线观看| 日韩三级电影| 青青草黄色视频| 五月天福利视频| 91成人在线影院| 老女人日逼视频| 大奶一区二区| 在线视频福利| 91在线日韩| 精品中文一区二区三区| 午夜无码高清| 日韩美女毛片| 水蜜桃视频在线| 天天干天天摸| 欧美日韩一区视频| 精品国产av| 成人网站AV| 国产精成人品| 日本在线观看www| 久色国产| 国产精品免费一区二区三区四区视频| 日韩激情无码一区二区| 男女性爱视频免费| 欧美XXX视频| 午夜免费播放观看在线视频| 欧美在线日韩| 精品无码一区二区三区四区久久久软件| 青青草原在线免费| 亚洲AV免费| 国产激情视频网站| 人人妻人人摸| 国产一区在线看| 91福利在线观看| 日韩欧美黄| 婷婷五月天在线观看| 中文字幕精品一区| 欧美视频操逼| 欧美级毛片一进一出夜本色| 欧美成人性爱网址| jjzz国产| 日韩三级视频在线观看| 免费无码成人片在线播放| 日韩第22页| 在线观看国产免费视频| 中文在线A∨在线| 中文字幕av在线观看| 久一久久| 北条麻妃无码在线| 亚洲男人天堂AV| 亚洲高清无码免费在线观看| 一本色道久久综合狠狠躁的推荐 | 97精品欧美91久久久久久久| 韩剧《邻居的妻子》电视剧 | 99黄网| 秋霞福利| 国产毛片在线视频| 99资源站| 日韩黄页网站| 国产熟女av| 日韩高清无码免费| 又大又粗又爽| 性欧美XXXX| 日韩黄色av| 欧美在线一级片| 日本免费黄色视频| 一级黄色片免费| 国产精品一区二区黑人巨大| 91无码电影| 日韩久久久久| 久久精品视频观看| 99久在线精品99re8热| 三级黄,色| 国产欧美在线| 亚洲高清成人动漫| 在线观看中文字幕亚洲| 午夜国产在线| 国产综合一区二区| 国精产品一区二区三区在线观看| av免费在线播放| 国产精品视频在线免费观看 | 美女做爱网站| 五月丁香婷婷激情| 婷婷丁香五月在线| 唐山熟女工棚嗷嗷叫| 99精品无码视频| 亚洲日韩一区二区三区四区| 亚洲AV成人片无码网站网蜜柚| 精品国产天线2024| 一道本视频在线| 99er热精品视频| 天天日,天天干,天天操| 成人免费A片视频| 大鸡巴在线| 国产一级A| 老熟女网站| 久久大香蕉视频| 爆乳尤物一区二区三区| 欧美日在线观看| 亚洲无码久久飞鱼网站| 免费一级做a爱片毛片A片小说 | 无码在线视频播放| 欧美黄色小说| 亚洲日韩视频在线| 人妻精品综合码| 亚洲无码操逼视频| 波多野结衣无码视频在线观看| 黄片小视频在线观看| 免费看国产黄色| 亚洲口味重一级黄片| 成人福利网站| 蜜桃Av噜噜一区| 亚洲成人精品一区| 日韩码线观看视频| 操人人| 韩国三级HD久久精品HD| 高清无码视频免费看| 91双飞会所双飞在线| 免费在线观看毛片| 日韩怡春院| 久草国产在线视频| 影音先锋男人| 欧洲尤物不卡播放六区| 亚洲区视频| 安徽妇搡BBBB搡BBBB袄爱直播| 成人小说亚洲一区二区三区| 亚洲天堂在线观看网站| 夜夜骚av.一区二区三区| 亚洲AV无码成人精品区| 天堂a在线| 真人一级片| 黄色成人毛片| 日韩综合在线| 在线超碰| 青青草在线播放| 北条麻妃精品在线| 麻豆秘在线观看国产| 国产天堂视频| 国产69精品久久久久久久久久久久| 一级欧美| A视频免费观看| 国产毛片一区| 久久久91人妻无码精品蜜桃ID| 蜜芽av在线观看| 欧美色成人免费在线视频| 人妻视频在线| 狠狠婷婷| 九色自拍视频| 国产精品成人免费视频| 黄片高清无码在线观看| 91精品少妇| 午夜成人福利视频| 亚洲综合五月天婷婷丁香| 国产无码高清视频| 七十路の高齢熟妇无码| 亚洲自拍网站| 琪琪av| 成人伊人电影| 大地影视官网第三页入口| 国产精品精品| 伊人网在线免费视频| 懂色av懂色av粉嫩av分享吧 | a片视频免费观看| 亚洲AV成人片色在线观看麻豆| 先锋影音在线资源| 91夫妻视频| 91人妻人人澡人人| 久久AV秘一区二区三区水生| 伦理被部长侵犯HD中字| 俺来俺去www色官网| 国产乱伦内射视频| 久久青青操| 欧一美一婬一伦一区二区三区 | 不卡视频一区二区三区| 日韩理论在线| 日韩欧美小电影| www.91madou| AV无码一区| 奇米av在线| 欧美日韩一级二级三级| AV中文字幕电影| 内射日韩| 成人A视频| 亚洲成人电影AV| 五月花在线视频| 老太奶性BBwBBw侧所| 久久中文字幕电影| 一级a片激情啪啪免费观| 国产黄色在线观看| 东京热在线观看| 精品一区二区三区三区| 伊人97| 蜜臀久久99精品久久久巴士| 中文字幕在线无码| 91青青草| 中文字幕免费在线观看| 人人做人人爱人人做人人乐的意思| 最近中文字幕免费MV第一季歌词十| 国产精品成人片| 国产精品无码在线观看| 51成人网站| 亚洲日韩AV在线| 在线观看国产| 狠狠的操| 国产清纯可爱美女自卫裸贷偷情 | av无码在线播放| 无码三级在线观看| 黄色三级视频在线观看| 伊人网综合| av无码aV天天aV天天爽| 久久婷婷五月综合| 秋霞福利| 日本高清色清di免费观看| 日韩精品一区二区三区四区| A片在线免费播放| 五月天亚洲无码| 大香蕉电影网站| 在线久操| 色小说在线| 国产又粗又猛又爽又黄91精品 | 亚洲成人五月天| 337p西西人体大胆瓣开下部| 少妇黄色视频| 日本免费黄色片| 中文字幕在线观看免费| 三级网站在线| 色伊人久操视频| 西西西444www无码视频| 成人国产无码| www.xxx国产| 亚洲永久免费精品| 国产传媒_色哟哟| 欧美图片小说| 日韩免费高清视频| 日本二区| 午夜成人三级| 大香焦伊人国产| 蜜桃人妻无码AV天堂二区| 3D动漫精品一区二区在线播放免费| 熟女中文| 国产亚洲一区二区三区| 99久久精品国产一区色| 一区二区无码视频| 无套免费视频欧美| 91精品国产综合久久久蜜臀粉嫩 | 日韩免费视频观看| 人人爽人人爱| 久久久久久久国产精品| 日韩操逼视频| 国产午夜影视| 国产精品小电影| 内射免费网站| 91免费国产视频| 国产麻豆视频| 欧美亚洲综合在线观看| 好男人一区二区三区在线观看| 成人久久av| 九九黄片| 亚洲在线免费| 成人免费毛片果冻日本| 在线观看黄色小电影| 影音先锋av在线资源| 欧美性性生交XXXXX无码| 91在线视频精品| 一区二区三区精品婷婷| 肏屄综合网| 日韩区一中文字幕a∨| 婷婷五月天成人电影| 黃色一级一片免费播放| 香蕉网站操逼片| 亚洲黄色视频在线观看网站| 69xx视频| 亚洲无码福利| AV无码国产| 欧美不卡在线观看| 黄色无遮挡| 日韩黄页网站| 欧美激情无码炮击| 美女高潮在线| 天天做天天爽| 亚洲av电影在线观看| 中文av网站| 亚洲免费黄| 国产精品国产精品国产专区| 久久人人操| 一区二区av在线| 99性爱| 四川BBBB擦BBBB| 欧美在线观看一区二区| 亚洲A片电影| 日韩综合久久| 中文字幕av高清片,中文在线观看| 亚洲丁香五月天| 无码在线观看免费视频| 女人久久| 97色色婷婷五月天| 天天日天天摸| 成人网站在线免费看| 丁香五月亭亭| 东方av在线播放| www.国产在线观看| 操人| 亚洲AV综合网| 草逼网站| 91无码高清视频| 久久久精品免费视频| 国产1区| 午夜久久福利| av怡红院| 精品人妻一区二区三区四区| 另类老妇性BBwBBw图片| 久久久久久成人电影| 黄色爱爱| 国产黄色视频在线观看免费| 午夜激情在线观看| 操b视频免费| 亚洲日韩成人电影| 99热在线观看| 91久久久久久久18| AV一级片| 婚闹不堪入目A片| 日韩AV中文| 婷婷五月天激情俺来也| 69人人| 天天日天天干天天日| 国产精品久久77777| 老女人日逼视频| 怡春院综合成人社区| 久久久久久久久久国产精品| 九九国产视频| 五月天婷婷小说| 免费无码婬片AAAA片直播| 国产91探花系列在线观看| 日韩激情一区| 国产黄色免费网站| 成人一区二区在线观看| 日本免费中文字幕| 日韩无码中文字| 丰满人妻精品一区二区在线 | 天天操天天射天天日| 欧美色小说| 97国产精品久久| 亚洲成人性爱| 日韩成人无码电影网站| 亚洲成人视屏| 日本视频在线免费| 在线播放JUY-925被丈夫上司侵犯的第7天| 无码精品一区二区三区在线观看| 中文字幕+乱码+中文字幕电视剧| 手机看片福利永久| 精品无码三级在线观看视频| 日韩肏屄视频在线观看| 色老板最新地址| 丁香五月天婷婷| 日韩免费小视频| 欧美一级a| 少妇搡BBBB搡BBB搡AA| 影音先锋一区| 天干夜天干天天天爽视频| 操逼片| 日韩成人观看| a在线观看免费| 国产精品a久久久久| 久久精品国产AV一区二区三区 | 日韩爆乳一区二区三区| 日日夜夜精品| 影音av资源| 精品国产乱子伦一区二区三区,小小扐| 久久精品内射| 亚洲精品久久久久毛片A级牛奶 | 午夜爱爱免费视频| 一本道高清无码视频| 日韩亚洲精品中文字幕| 北条麻妃在线无码| 无码中文AV| www.色在线观看| 精品乱子伦一区二区三区免费播成 | 亚洲精品无码免费| 三级片无码| 99热在线只有精品| 91麻豆精品国产91久久久久久久久 | AV777777| 色综合色| 亚洲AV永久无码成人| 精品欧美片在线观看步骤| 91精品国产aⅴ一区二区| 在线观看AⅤ| 成人性爱AV| 日韩欧美在线中文字幕| 国产浮力草草| 日韩三级在线观看| 女邻居的B好大| 日韩在线视频免费观看| 天天插天天日| 成年人免费看视频| 黄色毛片视频| 国产sm视频| 国产激情内射| 国产乱码一区二区三区四区在线| 特一级黄色视频| 大香蕉伊人成人| 日韩成人网址| 99re国产视频| 人人爽亚洲AV人人爽AV人人片 | 亚洲欧洲久久电影| 欧美超碰在线| 国产无码观看| 国产免费一区二区三区免费视频 | 天天艹夜夜| 久久黄色成人视频| 91成人影片| 国产一级二级视频| 嗯啊在线视频| 2025精品偷拍视频| 国产精品操| 国产无码高清在线| 日韩三级片av| 荫蒂添出高潮A片视频| 国产毛片一区| www.91超碰在线| 人人妻人人澡人人爽人人| 国产欧美黄片| 爱逼爱操| 国产在线小视频| 日韩特级毛片| 成人免费在线电影| 欧一美一伦一A片| 国产免费无码| 欧美一级特黄A片免费看| 伊人免费成人视频| 这里视频很精彩免费观看电视剧最新 | 精品国产久久久久| 国产操逼视频| 91成人视频在线播放| 国产网址| 日本在线一级| 色一本| 国产激情av| 亚洲综合视频在线观看| 亚洲小穴| 五月天无码免费视频| 男女性爱视频免费| 性爱无码| 午夜福利成人视频| 成人在线免费电影| 在线观看亚洲中文字幕| 夜夜网站| 亚洲黄色av| 久久爱成人| 成人超碰| 黄色片a片| 亚洲天堂视频网站| 超碰在线最新| 国产一区二区做爱| 99视频网| 黄色成人网站在线| 青青草手机在线观看| 色色三区| 偷自拍| 亚洲AV无码日韩AV无码导航| 蜜桃一区二区三区| 国产精品国产精品国产专区不片 | 天堂无码在线| 北条麻妃日B视频| 国产成人精品麻豆| 久久精品五月天| 日韩免费性爱视频| 国产免费操逼| 日韩视频免费观看高清完整版在线观| 亚洲午夜AV久久乱码| 中文字幕-区二区三区四区视频中国| 成年人黄色视频在线观看| 人妻天天爽夜夜爽| 色欲av网站| 麻豆午夜福利视频| 国产高清激情| 激情五月天小说网| 黄色毛片,男人天堂| 日本无码一区二区| 在线视频第一页| 免费网站观看www在线观看| 可以在线观看的av| 亚洲成人无码在线播放| 中文在线观看免费视频| 黄片高清无码在线观看| 苍井空视频| 一级Av| 国产又粗又大又黄视频| 日本成人激情视频| 激情视频免费看| 日韩精品一区二区三区四区| 91精品视频在线| 97中文字幕| 天天日日天天| 国产乱码一区二区三区的解决方法 | 欧美老女人操逼视频| 激情色色| 免费电影日本黄色| 蜜芽成人精品久久久视频| 国产乱子伦一区二区三| 99久免费视频| 先锋AV资源站| 思思热免费视频| 国产伦精品一级A片视频夜夜| 婷婷五月六月| 亚洲最新在线观看| 久久久久中文字幕| 影音先锋AV天堂| 亚洲福利在线免费观看| 四虎亚洲| 成人自拍电影| 麻豆AV免费看| 99久re热视频精品98| 亚欧洲精品在线视频| 狠狠干婷婷| 大香蕉啪啪视频|