【簡(jiǎn)單了解系列】從基礎(chǔ)的使用來(lái)深挖HashMap

HashMap定義

說(shuō)的專業(yè)一點(diǎn),HashMap是常用的用于存儲(chǔ)key-value鍵值對(duì)數(shù)據(jù)的一個(gè)集合魄鸦,底層是基于對(duì)Map的接口實(shí)現(xiàn)宴杀。每一個(gè)鍵值對(duì)又叫Entry,這些Entry分散的存儲(chǔ)在一個(gè)由數(shù)組和鏈表組成的集合中拾因。當(dāng)然在Java8中旺罢,Entry變成了Node

說(shuō)的通俗一點(diǎn)绢记,就像你去住酒店主经,你下單提供了你的手機(jī)號(hào),然后到酒店了給你一個(gè)房卡庭惜,你知道了你的房號(hào)之后再根據(jù)這個(gè)房號(hào)去找對(duì)應(yīng)的房間一樣罩驻。

房號(hào)就是key,房間里就是value护赊。你通過(guò)手機(jī)號(hào)下單到酒店給你房號(hào)可以理解為對(duì)key哈希的過(guò)程惠遏。你找的過(guò)程就是HashMap根據(jù)key取到對(duì)應(yīng)value的過(guò)程

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

table數(shù)組

首先我們要知道,我們存在HashMap中的數(shù)據(jù)最終是存了什么地方骏啰,就是如下的結(jié)構(gòu)节吮。

transient HashMap.Node<K, V>[] table;

可能有人看到transient有些陌生,被這個(gè)關(guān)鍵字修飾的變量將不會(huì)被序列化判耕。簡(jiǎn)單來(lái)說(shuō)透绩,就是序列化之后這個(gè)字段的值就會(huì)被干掉,用于一些不需要傳遞給第三方的字段壁熄。

例如一個(gè)矩形帚豪,在本地使用的時(shí)候,有長(zhǎng)草丧、寬和面積三個(gè)屬性狸臣,但是你要把這個(gè)對(duì)象給第三方用,但是由于面積可以通過(guò)另外兩個(gè)屬性推導(dǎo)出來(lái)昌执,這個(gè)key就不需要傳遞給第三方了烛亦。

這種情況就可以用transient關(guān)鍵字修飾诈泼。總的來(lái)說(shuō)就是煤禽,被transient修飾的變量將不再參與序列化铐达。

Node節(jié)點(diǎn)

下面是Node節(jié)點(diǎn)的定義。

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

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

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

上面的代碼省略了一些GetterSetter檬果,結(jié)構(gòu)還是非常清晰和簡(jiǎn)單娶桦。可以看到這個(gè)節(jié)點(diǎn)存儲(chǔ)了下一個(gè)節(jié)點(diǎn)的對(duì)象的引用汁汗,形成了一個(gè)鏈表的結(jié)構(gòu)衷畦。

為什么要用鏈表?用數(shù)組不行嗎知牌?剛剛上面提到過(guò)祈争,這個(gè)集合是由鏈表和數(shù)組組成的。因?yàn)樵偻昝赖膆ash算法都有可能產(chǎn)生哈希沖突角寸,所以兩個(gè)不同key的元素可以被放在同一個(gè)地方菩混。

而單用數(shù)組明顯不能滿足這個(gè)需求,而在數(shù)組的槽位上存一個(gè)鏈表就可以解決這個(gè)問(wèn)題扁藕。

HashMap的使用

上面簡(jiǎn)單了解了HashMap的定義和基本的底層數(shù)據(jù)結(jié)構(gòu)沮峡,接下來(lái)通過(guò)HashMap在平常開發(fā)中的使用來(lái)具體看看怎么實(shí)現(xiàn)的。

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

map.put("搜索關(guān)注公眾號(hào)", "SH的全棧筆記"); // 設(shè)置值
map.get("搜索關(guān)注公眾號(hào)");               // SH的全棧筆記 

賦值

put函數(shù)

上面的Put方法亿柑,我們傳入了兩個(gè)參數(shù)邢疙,Key和Value,函數(shù)的定義如下望薄。

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

應(yīng)該跟大多數(shù)人YY的put方法差不多疟游,put方法再調(diào)用了putVal 方法。

首先經(jīng)過(guò)了hash之后的key痕支,是一個(gè)整型的hashcode颁虐,其次是我們傳入的key和value。最后兩個(gè)布爾值卧须,后面會(huì)提到另绩。

首先一進(jìn)入putVal就會(huì)聲明存放數(shù)據(jù)的table,如果這個(gè)HashMap是首次設(shè)置值花嘶,就會(huì)被初始化一個(gè)默認(rèn)size的table笋籽,且所有元素的初始值都是NULL,下面是初始化這塊的核心代碼察绷,我省略掉了一些無(wú)關(guān)的變量聲明干签。

有趣的是津辩,初始化調(diào)用的是resize方法拆撼。

Node<K,V>[] tab; 
int n;
if ((tab = table) == null || (n = tab.length) == 0) {
  n = (tab = resize()).length;
}

newCap = 16; // 默認(rèn)容量
newThr = 12; // 默認(rèn)閾值

默認(rèn)值為啥是16

上面初始化table的默認(rèn)size給的是16容劳,當(dāng)然我們也可以自己定義,但是建議是最好是2的冪闸度。有的朋(杠)友(精)就要問(wèn)了竭贩,為什么是16呢?我13莺禁,14不他不香嗎留量?我們接下來(lái)就要分析為什么不香。

當(dāng)我們放元素進(jìn)入map的時(shí)候哟冬,它是如何確定元素在table數(shù)組中的位置的呢楼熄?我們拿搜索關(guān)注公眾號(hào)這個(gè)key舉例。

hash = (h = key.hashCode()) ^ h >>> 16
p = tab[i = n - 1 & hash]

可以看到浩峡,是將hash之后key和數(shù)組的length-1做與運(yùn)算得到了一個(gè)數(shù)組下標(biāo)可岂。而且,hash值的二進(jìn)制的位數(shù)翰灾,大多數(shù)情況下都會(huì)比table的長(zhǎng)度的二進(jìn)制位數(shù)多缕粹。換句話說(shuō),與運(yùn)算之后得到的數(shù)組下標(biāo)index完全取決于hash值的后幾位纸淮。

16 // n   10000
15 // n-1 1111
14 //     1110
13 //     1101
12 //     1100
11 //     1011
10 //     1010

從13平斩、14的二進(jìn)制值可以看出來(lái),存在0和1在二進(jìn)制位數(shù)上分布不均勻的情況咽块,這樣一來(lái)就會(huì)造成一個(gè)問(wèn)題绘面,那就是會(huì)存在某些不同的hash值經(jīng)過(guò)與運(yùn)算得到的值是一樣的。這樣就會(huì)導(dǎo)致hash到的index不均勻侈沪,換句話說(shuō)有些index可能永遠(yuǎn)都不會(huì)被hash到飒货,而有些index也被頻繁的hash到。

本來(lái)hash算法是要求計(jì)算的結(jié)果要均勻分布的峭竣,但是上述的結(jié)果明顯不符合均勻分布的要求塘辅。用n-1而不用n也是因?yàn)橥瑯拥牡览怼H绻@個(gè)值是2的冪皆撩,那么2的冪的值-1的所有二進(jìn)制位數(shù)都是1扣墩,這樣有利于hash計(jì)算的均勻分布。

綜上所述扛吞,不一定是16呻惕,2的冪都可以,16只是一個(gè)經(jīng)驗(yàn)值滥比。

自動(dòng)擴(kuò)容

除了size亚脆,初始化的時(shí)候還會(huì)設(shè)定一個(gè)閾值,值為12盲泛,newThr = 12濒持,這里需要提到一個(gè)概念負(fù)載因子键耕,HashMap的實(shí)現(xiàn)里默認(rèn)給的是0.75。

public HashMap() {
  this.loadFactor = 0.75F; // 12/16=0.75
}

負(fù)載因子是用來(lái)干嘛的呢柑营?最開始我們提到了屈雄,最開始存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,這種基礎(chǔ)結(jié)構(gòu)是有size設(shè)定的官套。當(dāng)我們不停的往map里存數(shù)據(jù)的時(shí)候酒奶,總會(huì)存滿,當(dāng)元素快存滿的時(shí)候奶赔,我們就需要擴(kuò)大map的容量惋嚎,來(lái)容納更多的元素,這就需要一個(gè)自動(dòng)擴(kuò)容的機(jī)制了站刑。

不是擴(kuò)容彈匣瘸彤,想啥呢

在當(dāng)數(shù)據(jù)量大于超過(guò)設(shè)定的閾值的時(shí)候(容量*負(fù)載因子),自動(dòng)對(duì)map進(jìn)行擴(kuò)容笛钝,以存放更多的數(shù)據(jù)质况。

自動(dòng)擴(kuò)容做了什么事情呢?總結(jié)來(lái)說(shuō)就是兩件事玻靡。

  • 創(chuàng)建新的數(shù)組结榄,大小是原來(lái)數(shù)組的一倍。
  • 將元素rehash到新的數(shù)組

為什么要rehash呢囤捻?上面我們提到過(guò)了臼朗,當(dāng)元素被放進(jìn)map時(shí),確認(rèn)下標(biāo)的方法是table的長(zhǎng)度-1hash值做與運(yùn)算蝎土,現(xiàn)在table的長(zhǎng)度發(fā)生了變化视哑,那么自然而然,元素獲取下標(biāo)的運(yùn)算結(jié)果也就跟之前的不一樣了誊涯, 所以需要將老的map中的元素再按照新的table長(zhǎng)度rehash到擴(kuò)容后的table中挡毅。

所以在當(dāng)你對(duì)性能有一定要求,且你知道你創(chuàng)建map的時(shí)候size的時(shí)候暴构,可以指定size跪呈,這樣一來(lái)就不會(huì)因?yàn)閿?shù)據(jù)量持續(xù)的增大而去頻繁的自動(dòng)擴(kuò)容了

put的過(guò)程中到底發(fā)生了什么

了解了底層數(shù)據(jù)結(jié)構(gòu)自動(dòng)擴(kuò)容機(jī)制,接下來(lái)我們來(lái)看一下put過(guò)程中究竟發(fā)生了什么取逾。我們上面說(shuō)過(guò)了耗绿,會(huì)通過(guò)數(shù)組的長(zhǎng)度-1hash值與運(yùn)算得到一個(gè)數(shù)組下標(biāo)。

如果該位置沒有元素砾隅,那么就很簡(jiǎn)單误阻,直接新建一個(gè)節(jié)點(diǎn)即可然后放置在數(shù)據(jù)的具體位置即可。

tab[i] = this.newNode(hash, key, value, (HashMap.Node)null);

但是如果該下標(biāo)已經(jīng)有元素了,這種情況HashMap是怎么處理的呢究反?這也要看情況寻定。

  • 如果是跟當(dāng)前槽位相同的key,就直接覆蓋奴紧。這就是我們修改某個(gè)key的值會(huì)發(fā)生的情況特姐。那HashMap怎么來(lái)判斷是不是同一個(gè)key呢晶丘?就像下面這樣黍氮。p就是當(dāng)前槽位上已經(jīng)有的元素,如果新浅浮、老元素的key的hashCode都相同且key不為空沫浆,那么就能證明這兩個(gè)key是相同的,那么此時(shí)只需要覆蓋即可滚秩。

    p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))

  • 而如果p是TreeNode的實(shí)例专执,那么就代表當(dāng)前槽位已經(jīng)是一個(gè)紅黑樹了,此時(shí)只需要往這個(gè)樹里putTreeVal即可郁油。至于為什么是紅黑樹本股,哪兒來(lái)的紅黑樹,下面馬上就要講到了桐腌。

  • 最后一種情況就是拄显,既不是已經(jīng)存在的元素也不是TreeNode的實(shí)例,也不是紅黑樹案站。這種情況下躬审,它就是一個(gè)普通的Node。你可以理解為鏈表蟆盐,如果hash沖突了承边,就把這個(gè)Node放到該位置的鏈表末尾。Java8之前采用的頭插法石挂,而Java8換成了尾插法博助,至于為什么要換,后面會(huì)講痹愚。

當(dāng)該位置的鏈表中的元素超過(guò)了TREEIFY_THRESHOLD所設(shè)置的數(shù)量時(shí)翔始,就會(huì)觸發(fā)樹化,將其轉(zhuǎn)化為紅黑樹里伯。Java8里給的默認(rèn)值是8城瞎。

為啥要轉(zhuǎn)化成紅黑樹

首先我們要知道為什么要樹化。當(dāng)大量的數(shù)據(jù)放入Map中疾瓮,Hash沖突會(huì)越來(lái)越多脖镀,某些位置就會(huì)出現(xiàn)一個(gè)很長(zhǎng)的鏈表的情況。這種情況下,查詢時(shí)間復(fù)雜度是O(n) 蜒灰,刪除的時(shí)間復(fù)雜度也是O(n)弦蹂,查詢、刪除的效率會(huì)大大降低强窖。而同樣的數(shù)據(jù)情況下凸椿,平衡二叉樹的時(shí)間復(fù)雜度都是O(logn)。

有的朋(杠)友(精)看到這個(gè)小標(biāo)題不樂(lè)意了翅溺,怎么就直接用紅黑樹了脑漫?我用二叉查找樹它不香嗎?

不了解二叉查找樹的咙崎,我把它的特點(diǎn)列在了下面优幸。

  • 左子樹上的所有節(jié)點(diǎn)的值都小于根節(jié)點(diǎn)的值

  • 右子樹上的所有節(jié)點(diǎn)的值都大于根節(jié)點(diǎn)的值

再精簡(jiǎn)一下就是,左小右大

但是褪猛,如果數(shù)據(jù)大量的趨近于有序网杆,例如所有的節(jié)點(diǎn)都比根節(jié)點(diǎn)大,那這個(gè)時(shí)候二叉查找樹就退化成了鏈表伊滋,查詢效率就會(huì)急劇下降碳却。看到這是不是覺得有點(diǎn)不對(duì)笑旺,我才從鏈表樹化昼浦,你這又給我退化成了鏈表荔茬?

朋友看到這又不樂(lè)意了君账,好好好,就算二叉查找樹不行输吏,那AVL樹它也不行物舒?用了AVL樹就不會(huì)出現(xiàn)上面所描述的效率急劇退化的情況了不是嗎色洞?

的確是這樣,AVL也可以叫平衡二叉搜索樹冠胯。AVL樹會(huì)在其有退化成鏈表的趨勢(shì)的時(shí)候(左右子樹的高度差超過(guò)某個(gè)閾值)調(diào)整樹的結(jié)構(gòu)火诸,也就是通過(guò)左旋和右旋來(lái)使其左右子樹的高度盡量平衡。

OK荠察,OK置蜀,就算你解釋清楚了為什么要樹化,那為什么一定要用紅黑樹悉盆?

具體的細(xì)節(jié)也就不在這里贅述盯荤,不知不覺已經(jīng)寫了這么多了,直接說(shuō)結(jié)論吧焕盟。AVL樹的查找速度更快秋秤,但是相應(yīng)的插入和修改的速度較慢。而紅黑樹則在插入和修改操作較為密集的時(shí)候表現(xiàn)更好。

而總結(jié)我們?nèi)粘5腍ashMap使用灼卢,大多數(shù)情況下插入和修改應(yīng)該是比查找更頻繁一些的绍哎。而在這種情況下,紅黑樹的綜合表現(xiàn)會(huì)更好一些鞋真。

至于紅黑樹的相關(guān)細(xì)節(jié)崇堰,涉及的東西還是挺多,我之后會(huì)單獨(dú)拿一個(gè)篇幅來(lái)講涩咖。

為什么要用尾插法

我們目前用的最多的是Java8海诲,在Java8中采用的是尾插法,Java8之前采用的是頭插法抠藕。

那為什么后面又變成了尾插法呢饿肺?放心蒋困,肯定不是設(shè)計(jì)者閑的蛋疼盾似,沒事來(lái)改個(gè)設(shè)計(jì)。這樣做一定是有一定的道理的雪标。在解釋這個(gè)問(wèn)題之前零院,我們先來(lái)看看,如果采取頭插法在多線程下的情況下會(huì)出現(xiàn)什么問(wèn)題村刨。

我們講過(guò)告抄,假設(shè)數(shù)組中index=1的位置已經(jīng)有了元素A,之后又有元素B被分配到了index=1的位置嵌牺。那么在下標(biāo)為1的槽位上的鏈表就變成了B -> A打洼。

此時(shí)再分配了一個(gè)新元素C,鏈表又被更新成了C -> B -> A逆粹。這也是為什么叫頭插法募疮,新的元素會(huì)被放在鏈表的頭節(jié)點(diǎn),因?yàn)楫?dāng)時(shí)設(shè)計(jì)的時(shí)候考慮到后被放入map的元素被訪問(wèn)的可能性更大僻弹。

上面講到了在當(dāng)不停的往map中放置元素后阿浓,超過(guò)了設(shè)定的閾值,就會(huì)觸發(fā)自動(dòng)擴(kuò)容蹋绽。此時(shí)會(huì)觸發(fā)兩個(gè)操作芭毙,一是創(chuàng)建一個(gè)容量為之前兩倍的底層數(shù)組,并且將老的數(shù)組中的元素rehash到新的數(shù)組中卸耘。

而由于數(shù)組的長(zhǎng)度發(fā)生了變化退敦,這就導(dǎo)致了元素的rehash結(jié)果跟之前在老數(shù)組中的位置不一樣。

首先我們來(lái)模擬一下rehash的過(guò)程蚣抗,假設(shè)新的數(shù)組中下標(biāo)為2的槽位是空的侈百。

  • 首先元素C,被放置在了其他位置。

  • 然后元素B设哗,被rehash到了下標(biāo)為2的槽位唱捣, 至此都沒有問(wèn)題。

  • 最后元素A网梢,同樣被rehash到了下標(biāo)為2的槽位震缭,此時(shí)鏈表變成了A -> B。到這就有問(wèn)題了战虏,最開始B的next指向的是A節(jié)點(diǎn)拣宰。但是rehash之后A的next又指向B,看到這你應(yīng)該就能明白發(fā)生了什么烦感。

我看到很多的對(duì)JDK1.7版的HashMap在多線程的情況下擴(kuò)容會(huì)出現(xiàn)死鎖的解釋都只到了環(huán)形鏈表巡社。但是其實(shí)就算是環(huán)形鏈表,只要找到了對(duì)應(yīng)的元素手趣,就會(huì)直接退出循環(huán)的邏輯晌该,也不會(huì)造成死循環(huán)。

實(shí)際情況是绿渣,當(dāng)自動(dòng)擴(kuò)容形成了環(huán)形鏈表后朝群,當(dāng)你去Get了一個(gè)在entry鏈上不存在的元素時(shí),就會(huì)出現(xiàn)死循環(huán)的情況中符。

取值

上面聊了給HashMap賦值的大概過(guò)程姜胖,接下來(lái)聊一下從HashMap獲取值會(huì)發(fā)生什么。get方法的開始淀散,跟put一樣很簡(jiǎn)單右莱。

public V get(Object key) {
  Node<K,V> e;
  return (e = getNode(hash(key), key)) == null ? null : e.value;
}

可以看到,取值的核心操作是getNode來(lái)負(fù)責(zé)完成的档插。

首先第一件事就是去check的第一個(gè)元素是不是當(dāng)前查找的元素慢蜓。

如果不是,而且當(dāng)前槽位已經(jīng)被樹化成了紅黑樹阀捅,就走紅黑樹的getTreeNode方法胀瞪。

如果還沒有被樹化,只是普通的鏈表饲鄙,則順著next一路找下去凄诞。

由于get方法邏輯和實(shí)現(xiàn)都比較容易理解,就不貼太多源碼了忍级。

結(jié)尾

由于最近太忙了帆谍,工作和生活中的事都巨多,這篇文章是幾周利用零零散散的時(shí)間寫出來(lái)的轴咱,如果有什么問(wèn)題汛蝙,歡迎大家在評(píng)論區(qū)討論烈涮。

好了以上就是本篇博客的全部?jī)?nèi)容了,如果你覺得這篇文章對(duì)你有幫助窖剑,還麻煩點(diǎn)個(gè)贊坚洽,關(guān)個(gè)注,分個(gè)享西土,留個(gè)言讶舰。

歡迎微信搜索關(guān)注【SH的全棧筆記】,查看更多相關(guān)文章

拜了個(gè)拜


image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末需了,一起剝皮案震驚了整個(gè)濱河市跳昼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肋乍,老刑警劉巖鹅颊,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異墓造,居然都是意外死亡堪伍,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門滔岳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杠娱,“玉大人挽牢,你說(shuō)我怎么就攤上這事谱煤。” “怎么了禽拔?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵刘离,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我睹栖,道長(zhǎng)硫惕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任野来,我火速辦了婚禮恼除,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘曼氛。我一直安慰自己豁辉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布舀患。 她就那樣靜靜地躺著徽级,像睡著了一般。 火紅的嫁衣襯著肌膚如雪聊浅。 梳的紋絲不亂的頭發(fā)上餐抢,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天现使,我揣著相機(jī)與錄音,去河邊找鬼旷痕。 笑死碳锈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的欺抗。 我是一名探鬼主播殴胧,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼佩迟!你這毒婦竟也來(lái)了团滥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤报强,失蹤者是張志新(化名)和其女友劉穎灸姊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秉溉,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡力惯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了召嘶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片父晶。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖弄跌,靈堂內(nèi)的尸體忽然破棺而出甲喝,到底是詐尸還是另有隱情,我是刑警寧澤铛只,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布埠胖,位于F島的核電站,受9級(jí)特大地震影響淳玩,放射性物質(zhì)發(fā)生泄漏直撤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一蜕着、第九天 我趴在偏房一處隱蔽的房頂上張望谋竖。 院中可真熱鬧,春花似錦承匣、人聲如沸蓖乘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驱敲。三九已至,卻和暖如春宽闲,著一層夾襖步出監(jiān)牢的瞬間众眨,已是汗流浹背握牧。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娩梨,地道東北人沿腰。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狈定,于是被迫代替她去往敵國(guó)和親颂龙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容