[轉]深入理解HashMap

初識HashMap

關于List荠瘪,ArrayList贡蓖、LinkedList圣贸,CopyOnWriteArrayList殴泰,就前兩者而言于宙,反映的是兩種思想:

(1)ArrayList以數(shù)組形式實現(xiàn),順序插入悍汛、查找快捞魁,插入、刪除較慢

(2)LinkedList以鏈表形式實現(xiàn)离咐,順序插入谱俭、查找較慢奉件,插入、刪除方便

那么是否有一種數(shù)據(jù)結構能夠結合上面兩種的優(yōu)點呢昆著?有县貌,答案就是HashMap。

HashMap是一種非常常見凑懂、方便和有用的集合煤痕,是一種鍵值對(K-V)形式的存儲結構,下面將還是用圖示的方式解讀HashMap的實現(xiàn)原理接谨。

四個關注點在HashMap上的答案

image

添加數(shù)據(jù)

首先看一下HashMap的一個存儲單元Entry:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">static class Entry<K,V> implements Map.Entry<K,V> {

final K key;

V value;

Entry<K,V> next;

int hash;

...

}</pre>

從HashMap的Entry看得出摆碉,Entry組成的是一個單向鏈表,因為里面只有Entry的后繼Entry脓豪,而沒有Entry的前驅Entry巷帝。用圖表示應該是這么一個數(shù)據(jù)結構:

image

接下來,假設我有這么一段代碼:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">public static void main(String[] args)

{

Map<String, String> map = new HashMap<String, String>();

map.put("111", "111");

map.put("222", "222");

}</pre>

看一下做了什么扫夜。首先從第3行開始楞泼,new了一個HashMap出來:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">public HashMap() {

this.loadFactor = DEFAULT_LOAD_FACTOR;

threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);

table = new Entry[DEFAULT_INITIAL_CAPACITY];

init();

}</pre>

注意一下第5行的init()是個空方法,它是HashMap的子類比如LinkedHashMap構造的時候使用的笤闯。DEFAULT_INITIAL_CAPACITY為16堕阔,也就是說,HashMap在new的時候構造出了一個大小為16的Entry數(shù)組望侈,Entry內所有數(shù)據(jù)都取默認值印蔬,如圖示為:

[圖片上傳中...(image-750d50-1532153177137-3)]

看到new出了一個大小為16的Entry數(shù)組來。接著第4行脱衙,put了一個Key和Value同為111的字符串,看一下put的時候底層做了什么:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">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;

}</pre>

static int hash(int h) {

// This function ensures that hashCodes that differ only by

// constant multiples at each bit position have a bounded

// number of collisions (approximately 8 at default load factor).

h ^= (h >>> 20) ^ (h >>> 12);

return h ^ (h >>> 7) ^ (h >>> 4);

}

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">static int indexFor(int h, int length) {

return h & (length-1);

}</pre>

看一下put方法的幾個步驟:

1例驹、第2行~第3行就是HashMap允許Key值為空的原因捐韩,空的Key會默認放在第0位的數(shù)組位置上

2、第4行拿到Key值的HashCode鹃锈,由于HashCode是Object的方法荤胁,因此每個對象都有一個HashCode,對這個HashCode做一次hash計算屎债。按照JDK源碼注釋的說法仅政,這次hash的作用是根據(jù)給定的HashCode對它做一次打亂的操作,防止一些糟糕的Hash算法產(chǎn)生的糟糕的Hash值盆驹,至于為什么要防止糟糕的Hash值圆丹,HashMap添加元素的最后會講到

3、第5行根據(jù)重新計算的HashCode躯喇,對Entry數(shù)組的大小取模得到一個Entry數(shù)組的位置辫封∠跬鳎看到這里使用了&,移位加快一點代碼運行效率倦微。另外妻味,這個取模操作的正確性依賴于length必須是2的N次冪,這個熟悉二進制的朋友一定理解欣福,因此注意HashMap構造函數(shù)中责球,如果你指定HashMap初始數(shù)組的大小initialCapacity,如果initialCapacity不是2的N次冪拓劝,HashMap會算出大于initialCapacity的最小2的N次冪的值棕诵,作為Entry數(shù)組的初始化大小。這里為了講解方便凿将,我們假定字符串111和字符串222算出來的i都是1

4校套、第6行~第14行會先判斷一下原數(shù)據(jù)結構中是否存在相同的Key值,存在則覆蓋并返回牧抵,不執(zhí)行后面的代碼笛匙。注意一下recordAccess這個方法,它也是HashMap的子類比如LinkedHashMap用的犀变,HashMap中這個方法為空妹孙。另外,注意一點获枝,對比Key是否相同蠢正,是先比HashCode是否相同,HashCode相同再判斷equals是否為true省店,這樣大大增加了HashMap的效率嚣崭,對HashCode不熟悉的朋友可以看一下我的這篇文章講講HashCode的作用

5、第16行的modeCount++是用于fail-fast機制的懦傍,每次修改HashMap數(shù)據(jù)結構的時候都會自增一次這個值

然后就到了關鍵的addEntry方法了:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">void addEntry(int hash, K key, V value, int bucketIndex) {

Entry<K,V> e = table[bucketIndex];

table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

if (size++ >= threshold)

resize(2 * table.length);

}</pre>

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">Entry(int h, K k, V v, Entry<K,V> n) {

value = v;

next = n;

key = k;

hash = h;

}</pre>

假設new出來的Entry地址為0×00000001雹舀,那么,put(“111″, “111″)用圖表示應該是這樣的:

[圖片上傳中...(image-b40ba0-1532153177137-2)]

每一個新增的Entry都位于table[1]上粗俱,另外说榆,里面的hash是rehash之后的hash而不是Key最原始的hash〈缛希看到table[1]上存放了111—->111這個鍵值對签财,它持有原table[1]的引用地址,因此可以尋址到原table[1]偏塞,這就是單向鏈表唱蒸。 再看一下put(“222″, “222″)做了什么,一張圖就可以理解了:

image

新的Entry再次占據(jù)table[1]的位置烛愧,并且持有原table[1]油宜,也就是111—->111這個鍵值對掂碱。

至此,HashMap進行put數(shù)據(jù)的過程就呈現(xiàn)清楚了慎冤。不過還有一個問題疼燥,就是HashMap如何進行擴容,再看一下addEntry方法:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">void addEntry(int hash, K key, V value, int bucketIndex) {

Entry<K,V> e = table[bucketIndex];

table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

if (size++ >= threshold)

resize(2 * table.length);

}</pre>

看到第4行~第5行蚁堤,也就是說在每次放置完Entry之后都會判斷是否需要擴容醉者。這里不講擴容是因為HashMap擴容在不正確的使用場景下將會導致死循環(huán),這是一個值得探討的話題披诗,也是我工作中實際遇到過的一個問題撬即,因此下一篇文章將會詳細說明為什么不正確地使用HashMap會導致死循環(huán)。

刪除數(shù)據(jù)

有一段代碼:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">public static void main(String[] args)

{

Map<String, String> map = new HashMap<String, String>();

map.put("111", "111");

map.put("222", "222");

map.remove("111");

}</pre>

第6行刪除元素呈队,看一下刪除元素的時候做了什么剥槐,第4行~第5行添加了兩個鍵值對就沿用上面的圖,HashMap刪除指定鍵值對的源代碼是:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">public V remove(Object key) {

Entry<K,V> e = removeEntryForKey(key);

return (e == null ? null : e.value);

}</pre>

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">final Entry<K,V> removeEntryForKey(Object key) {

int hash = (key == null) ? 0 : hash(key.hashCode());

int i = indexFor(hash, table.length);

Entry<K,V> prev = table[i];

Entry<K,V> e = prev;

while (e != null) {

Entry<K,V> next = e.next;

Object k;

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k)))) {

modCount++;

size--;

if (prev == e)

table[i] = next;

else

prev.next = next;

e.recordRemoval(this);

return e;

}

prev = e;

e = next;

}

return e;

}</pre>

分析一下remove元素的時候做了幾步:

1宪摧、根據(jù)key的hash找到待刪除的鍵值對位于table的哪個位置上

2粒竖、記錄一個prev表示待刪除的Entry的前一個位置Entry,e可以認為是當前位置

3几于、從table[i]開始遍歷鏈表蕊苗,假如找到了匹配的Entry,要做一個判斷沿彭,這個Entry是不是table[i]:

(1)是的話朽砰,也就是第14行~第15行,table[i]就直接是table[i]的下一個節(jié)點喉刘,后面的都不需要動

(2)不是的話瞧柔,也就是第16行~第17行,e的前一個Entry也就是prev饱搏,prev的next指向e的后一個節(jié)點非剃,也就是next,這樣推沸,e所代表的Entry就被踢出了,e的前后Entry就連起來了

remove(“111″)用圖表示就是:

image

整個過程只需要修改一個節(jié)點的next的值即可券坞,非常方便鬓催。

修改數(shù)據(jù)

修改元素也是put,看一下源代碼:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">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;

}</pre>

這個其實前面已經(jīng)提到過了恨锚,第6行~第14行就是修改元素的邏輯宇驾,如果某個Key已經(jīng)在數(shù)據(jù)結構中存在的話,那么就會覆蓋原value猴伶,也就是第10行的代碼课舍。

插入數(shù)據(jù)

所謂”插入元素”塌西,在我的理解里,一定是基于數(shù)據(jù)結構是有序的前提下的筝尾。像ArrayList捡需、LinkedList,再遠點說就是數(shù)據(jù)庫筹淫,一條一條都是有序的站辉。

而HashMap,它的順序是基于HashCode损姜,HashCode是一個隨機性很強的數(shù)字饰剥,所以HashMap中的Entry完全是隨機存放的。HashMap又不像LinkedHashMap這樣維護了插入元素的順序摧阅,所以對HashMap這個數(shù)據(jù)結構談插入元素是沒有意義的汰蓉。

所以,HashMap并沒有插入的概念棒卷。

再談HashCode的重要性

前面講到了顾孽,HashMap中對Key的HashCode要做一次rehash,防止一些糟糕的Hash算法生成的糟糕的HashCode娇跟,那么為什么要防止糟糕的HashCode岩齿?

糟糕的HashCode意味著的是Hash沖突,即多個不同的Key可能得到的是同一個HashCode苞俘,糟糕的Hash算法意味著的就是Hash沖突的概率增大盹沈,這意味著HashMap的性能將下降,表現(xiàn)在兩方面:

1吃谣、有10個Key乞封,可能6個Key的HashCode都相同,另外四個Key所在的Entry均勻分布在table的位置上岗憋,而某一個位置上卻連接了6個Entry肃晚。這就失去了HashMap的意義,HashMap這種數(shù)據(jù)結構性高性能的前提是仔戈,Entry均勻地分布在table位置上关串,但現(xiàn)在確是1 1 1 1 6的分布。所以监徘,我們要求HashCode有很強的隨機性晋修,這樣就盡可能地可以保證了Entry分布的隨機性,提升了HashMap的效率凰盔。

2墓卦、HashMap在一個某個table位置上遍歷鏈表的時候的代碼:

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

看到,由于采用了”&&”運算符户敬,因此先比較HashCode落剪,HashCode都不相同就直接pass了睁本,不會再進行equals比較了。HashCode因為是int值忠怖,比較速度非衬匮撸快,而equals方法往往會對比一系列的內容脑又,速度會慢一些暮胧。Hash沖突的概率大,意味著equals比較的次數(shù)勢必增多问麸,必然降低了HashMap的效率了往衷。

HashMap的table為什么是transient的

一個非常細節(jié)的地方:

transient Entry[] table;

看到table用了transient修飾,也就是說table里面的內容全都不會被序列化严卖,不知道大家有沒有想過這么寫的原因席舍?

在我看來,這么寫是非常必要的哮笆。因為HashMap是基于HashCode的来颤,HashCode作為Object的方法,是native的:

public native int hashCode();

這意味著的是:HashCode和底層實現(xiàn)相關稠肘,不同的虛擬機可能有不同的HashCode算法福铅。再進一步說得明白些就是,可能同一個Key在虛擬機A上的HashCode=1项阴,在虛擬機B上的HashCode=2滑黔,在虛擬機C上的HashCode=3。

這就有問題了环揽,Java自誕生以來略荡,就以跨平臺性作為最大賣點,好了歉胶,如果table不被transient修飾汛兜,在虛擬機A上可以用的程序到虛擬機B上可以用的程序就不能用了,失去了跨平臺性通今,因為:

1粥谬、Key在虛擬機A上的HashCode=100,連在table[4]上

2辫塌、Key在虛擬機B上的HashCode=101帝嗡,這樣,就去table[5]上找Key璃氢,明顯找不到

整個代碼就出問題了。因此狮辽,為了避免這一點一也,Java采取了重寫自己序列化table的方法巢寡,在writeObject選擇將key和value追加到序列化的文件最后面:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">private void writeObject(java.io.ObjectOutputStream s)

throws IOException

{

Iterator<Map.Entry<K,V>> i =

(size > 0) ? entrySet0().iterator() : null;

// Write out the threshold, loadfactor, and any hidden stuff

s.defaultWriteObject();

// Write out number of buckets

s.writeInt(table.length);

// Write out size (number of Mappings)

s.writeInt(size);

// Write out keys and values (alternating)

if (i != null) {

while (i.hasNext()) {

Map.Entry<K,V> e = i.next();

s.writeObject(e.getKey());

s.writeObject(e.getValue());

}

}

}</pre>

而在readObject的時候重構HashMap數(shù)據(jù)結構:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">private void readObject(java.io.ObjectInputStream s)

throws IOException, ClassNotFoundException

{

// Read in the threshold, loadfactor, and any hidden stuff

s.defaultReadObject();

// Read in number of buckets and allocate the bucket array;

int numBuckets = s.readInt();

table = new Entry[numBuckets];

init(); // Give subclass a chance to do its thing.

// Read in size (number of Mappings)

int size = s.readInt();

// Read the keys and values, and put the mappings in the HashMap

for (int i=0; i<size; i++) {

K key = (K) s.readObject();

V value = (V) s.readObject();

putForCreate(key, value);

}

}</pre>

一種麻煩的方式,但卻保證了跨平臺性椰苟。

這個例子也告訴了我們:盡管使用的虛擬機大多數(shù)情況下都是HotSpot抑月,但是也不能對其它虛擬機不管不顧,有跨平臺的思想是一件好事舆蝴。

HashMap和Hashtable的區(qū)別

HashMap和Hashtable是一組相似的鍵值對集合谦絮,它們的區(qū)別也是面試常被問的問題之一,我這里簡單總結一下HashMap和Hashtable的區(qū)別:

1洁仗、Hashtable是線程安全的层皱,Hashtable所有對外提供的方法都使用了synchronized,也就是同步赠潦,而HashMap則是線程非安全的

2叫胖、Hashtable不允許空的value,空的value將導致空指針異常她奥,而HashMap則無所謂瓮增,沒有這方面的限制

3、上面兩個缺點是最主要的區(qū)別哩俭,另外一個區(qū)別無關緊要绷跑,我只是提一下,就是兩個的rehash算法不同凡资,Hashtable的是:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">private int hash(Object k) {

// hashSeed will be zero if alternative hashing is disabled.

return hashSeed ^ k.hashCode();

}</pre>

這個hashSeed是使用sun.misc.Hashing類的randomHashSeed方法產(chǎn)生的砸捏。HashMap的rehash算法上面看過了,也就是:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 15px; display: block; padding: 12.5px; margin: 0px 0px 13px; line-height: 1.625; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 3px;">static int hash(int h) {

// This function ensures that hashCodes that differ only by

// constant multiples at each bit position have a bounded

// number of collisions (approximately 8 at default load factor).

h ^= (h >>> 20) ^ (h >>> 12);

return h ^ (h >>> 7) ^ (h >>> 4);

}</pre>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末讳苦,一起剝皮案震驚了整個濱河市带膜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸳谜,老刑警劉巖膝藕,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咐扭,居然都是意外死亡,警方通過查閱死者的電腦和手機蝗肪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門袜爪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腊状,你說我怎么就攤上這事。” “怎么了同蜻?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵棚点,是天一觀的道長。 經(jīng)常有香客問我埃仪,道長乙濒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任卵蛉,我火速辦了婚禮颁股,結果婚禮上,老公的妹妹穿的比我還像新娘傻丝。我一直安慰自己甘有,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布葡缰。 她就那樣靜靜地躺著亏掀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泛释。 梳的紋絲不亂的頭發(fā)上滤愕,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音怜校,去河邊找鬼间影。 笑死,一個胖子當著我的面吹牛茄茁,可吹牛的內容都是我干的魂贬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼裙顽,長吁一口氣:“原來是場噩夢啊……” “哼付燥!你這毒婦竟也來了?” 一聲冷哼從身側響起愈犹,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤键科,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萝嘁,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡梆掸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了牙言。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡怪得,死狀恐怖咱枉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情徒恋,我是刑警寧澤蚕断,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站入挣,受9級特大地震影響亿乳,放射性物質發(fā)生泄漏。R本人自食惡果不足惜径筏,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一葛假、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧滋恬,春花似錦聊训、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勋拟,卻和暖如春勋磕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背敢靡。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工挂滓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人醋安。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓杂彭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吓揪。 傳聞我的和親對象是個殘疾皇子亲怠,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容

  • 1.HashMap是一個數(shù)組+鏈表/紅黑樹的結構,數(shù)組的下標在HashMap中稱為Bucket值柠辞,每個數(shù)組項對應的...
    誰在烽煙彼岸閱讀 1,025評論 2 2
  • 實際上团秽,HashSet 和 HashMap 之間有很多相似之處,對于 HashSet 而言,系統(tǒng)采用 Hash 算...
    曹振華閱讀 2,513評論 1 37
  • 一、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對于byte類型而言...
    龍貓小爺閱讀 4,261評論 0 16
  • 5.1图毕、對于HashMap需要掌握以下幾點 Map的創(chuàng)建:HashMap() 往Map中添加鍵值對:即put(Ob...
    rochuan閱讀 678評論 0 0
  • 有時命運會左右一個人夷都,不相信命運也會經(jīng)歷不幸。 一 門前的柳樹垂著萬條絲絳予颤,像一個少女滿頭的小麻花辮子囤官,煞是好看。...
    華客魯俊閱讀 610評論 0 1