HashMap源碼解析看多了面試還是不會說箕速?看這篇文章就夠了

本來今天想要重新整理一些hashMap的源碼閱讀解析文章的相關內容的叼风,后面發(fā)現(xiàn)網上關于HashMap的源碼解析已經有很多專業(yè)的分析了,在看了一遍源碼之后休蟹,我就直接整理一下HashMap相關的一些熱門面試題的答案吧沸枯。

在回答以下的面試題之前日矫,還是希望對HashMap有以下的一些很基礎性的認識。

  1. HashMap 允許Key绑榴、Value 同時為空
  2. 線程不安全
  3. HashMap是使用一個Node<K,V> tables數(shù)組哪轿,里面包含hash、key翔怎、value窃诉、Node next,存放數(shù)據(jù)赤套,存儲結構要么為鏈表飘痛,要么為紅黑樹。假設數(shù)組的索引為N容握,里面的Node進行put時宣脉,如果put之后該索引下的鏈表長度大于8,會根據(jù)判斷tables數(shù)組的長度是否大于64剔氏,才會轉換存儲結構為紅黑樹塑猖。否則只會執(zhí)行擴容。
    4.進行pop時谈跛,如果該索引中存儲結構為紅黑樹羊苟,其存儲長度<=6,又會轉化為鏈表的數(shù)據(jù)結構存儲感憾。
  4. hashMap數(shù)組的初始化之后長度為16.
  5. 負載因子蜡励,默認為0.75,也可以自己設置吹菱。是時間和空間之間的權衡巍虫,后面會詳細說,這里需要知道鳍刷,如果我們數(shù)組初始化長度為12占遥,通過16負載因子的值得到12,當我們數(shù)組的長度超過12時输瓜,就需要進行擴容瓦胎,每次擴容都是2倍擴容。

面試的問題總結:

從以下幾點思路去入手分析:

size必須是2的整數(shù)次方原因

這個根據(jù)我有去看源碼的思路的理解是這樣的尤揣,在擴容時搔啊,每次都是new一個新的原來兩倍的數(shù)組長度大小的node<k,v>[]數(shù)組,然后再散列的把原來的數(shù)據(jù)加入新數(shù)組北戏。

在put的時候负芋,每一個key,都會對應到一個桶里面嗜愈,通過index = hash & (n-1)計算桶的索引旧蛾。設原來的數(shù)組為table莽龟,負載因子為默認的0.75,長度n為16.則當里面的數(shù)據(jù)量大于12時锨天,需要進行擴容毯盈。就會新建一個長度為16*2的數(shù)組為newTab。然后就會開始把原來數(shù)組tables里面的數(shù)據(jù)搬移到newTab病袄,假設tables的第i個索引搂赋,通過索引的計算公式: hash & n(n為數(shù)組長度) ,對比新舊數(shù)組會發(fā)現(xiàn)一個規(guī)律,當我們把tabsles[i]的node遷到newTab時益缠,里面的node數(shù)據(jù)要么在原來的i位置不變脑奠,要么就在i+n的位置,所以在put時這樣處理把左刽,根據(jù)該公式的值捺信,把tables的數(shù)據(jù)拆分成兩個鏈表L1以及L2酌媒,執(zhí)行一次tables[i]下面的元素遍歷欠痴,令newtab[i]=l1,newtab[i+n]=l2,這樣就會完成tables[i]位置上面所有node的遷移了/refresh,這就是為什么設置容量為什么要為2的整數(shù)次冪帶來的方便之處秒咨。resize的邏輯就是上面講的那樣喇辽。將table[i]處的Node拆分為兩個鏈表,這兩個鏈表再放到newtab[i]和newtab[i+n]位置

get方法的流程

內部會執(zhí)行一個getNode方法判斷的雨席,入?yún)⑹莻魅雓ey的hash計算后的一個整數(shù)類型hash以及key菩咨。內部流程如下:

  1. 根據(jù) (n-1) & hash的算法得到桶的索引(這個桶要詳細解釋的話,就是hashMap里面存放數(shù)據(jù)的table數(shù)組的某一個索引里面的包含的數(shù)據(jù)結構陡厘,初始化是有16個桶)抽米,獲取出的該桶里面的node。
  2. 匹配該node值是都是要找的key糙置,如果是則直接返回云茸。
    3.否則,先判斷node是否為TreeNode谤饭,也就是紅黑樹類型的node标捺,如果是,則調用紅黑樹的getTreeNode去獲取里面的值揉抵。
    4.如果不是亡容,執(zhí)行鏈表查找,直到找到冤今。

附上get(key)源碼

public V get(Object key) {
        Node<K,V> e;
        
        //hash:這個函數(shù)上面分析過了闺兢。返回key混淆后的hashCode
        //注意getNode返回的類型是Node:當返回值為null時表示map中沒有對應的key,注意區(qū)分value為
        //null:如果key對應的value為null的話戏罢,體現(xiàn)在getNode的返回值e.value為null屋谭,此時返回值也是
        //null阱佛,也就是HashMap的get函數(shù)不能判斷map中是否有對應的key:get返回值為null時硕淑,可能不包 
        //含該key咕别,也可能該key的value為null!那么如何判斷map中是否包含某個key呢防嗡?見下面contains            
        //函數(shù)分析
        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;
        //(n-1)&hash:當前key可能在的桶索引所意,put操作時也是將Node存放在index=(n-1)&hash位置
        //主要邏輯:如果table[index]處節(jié)點的key就是要找的key則直接返回該節(jié)點淮逊;
        //否則:如果在table[index]位置進行搜索,搜索是否存在目標key的Node:這里的搜索又分兩種:
        //鏈表搜索和紅黑樹搜索扶踊,具體紅黑樹的查找就不展開了泄鹏,紅黑樹是一種弱平衡(相對于AVL)BST,
        //紅黑樹查找秧耗、刪除备籽、插入等操作都能夠保證在O(lon(n))時間復雜度內完成,紅黑樹原理不在本文
        //范圍內分井,但是大家要知道紅黑樹的各種操作是可以實現(xiàn)的车猬,簡單點可以把紅黑樹理解為BST,
        //BST的查找尺锚、插入珠闰、刪除等操作的實現(xiàn)在之前的博客中有java實現(xiàn)代碼,實際上紅黑樹就是一種平            
        //衡的BST
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;//一次就匹配到了瘫辩,直接返回伏嗜,否則進行搜索
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    //紅黑樹搜索/查找
                    return ((TreeNode<K,V>)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;//沒找到,返回null
    }

put方法流程

具體流程如下: 內部會執(zhí)行一個putVal的方法伐厌,參數(shù)分別為( hash(key) , key , value , onlyIfAbsent,evict).會稍微復雜一點:
1.如果hashMap里面還未初始化承绸,則初始化resize();

  1. 如果通過索引定位算法 (n-1) & hash 找到的桶為null,則直接構建一個node挣轨,set進去军熏。
  2. 如果定位到的tabls[index]有元素了,先判斷該元素key是否與存入的key相等刃唐,享的則set進去羞迷,否則判斷是否為紅黑樹類型,使用紅黑樹的方式put進去putTreeVal画饥。
    4.以上都不是衔瓮,則在該桶的位置開始遍歷鏈表,直到鏈表的末尾抖甘,查找是否存在該key热鞍,構建node。
    5.判斷遍歷的次數(shù),如果大于等于8個薇宠,則執(zhí)行是否進行轉為紅黑樹結構存儲的判斷偷办。
  3. 判斷當前的key是否已存在,存在則更新key澄港,并且有一個鉤子函數(shù)椒涯,未實現(xiàn),然后會返回舊的key的value值回梧。
    7.如果是第一次插入废岂,則會執(zhí)行到failfast機制,不會返回已存在的值狱意,此時也已經put進去了湖苞。
    8.判斷當前的hashMap里面的數(shù)量是否大于負載因子計算后的值,需要進行擴容详囤。
  4. return null

見源碼如下:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //上面提到過HashMap是懶加載财骨,所有put的時候要先檢查table數(shù)組是否已經初始化了,
        //沒有初始化得先初始化table數(shù)組藏姐,保證table數(shù)組一定初始化了
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;//這個函數(shù)后面有resize函數(shù)分析
    
        //到這里表示table數(shù)組一定初始化了
        //與上面get函數(shù)相同隆箩,指定key的Node,put在table數(shù)組的i=(n-1)&hash下標位置包各,get的時候
        //也是從table數(shù)組的該位置搜索
        if ((p = tab[i = (n - 1) & hash]) == null)
            //如果i位置還沒有存儲元素摘仅,則把當前的key靶庙,value封裝為Node问畅,存儲在table[i]位置
            tab[i] = newNode(hash, key, value, null);
        else {
       //如果table[i]位置已經有元素了,則接下來執(zhí)行的是:
       //首先判斷鏈表或者二叉樹中時候已經存在key的鍵值對六荒,存在的話就更新它的value
       //不存在的話把當前的key护姆,value插入到鏈表的末尾或者插入到紅黑樹中
       //如果鏈表或者紅黑樹中已經存在Node.key等于key,則e指向該Node掏击,即
       //e指向一個Node:該Node的key屬性與put時傳入的key參數(shù)相等的那個Node卵皂,后面會更新e.value
            Node<K,V> e; K k;

       //為什么get和put先判斷p.hash==hash,下面的if條件中去掉hash的比較也可以邏輯也正確?
       //因為hash的比較是兩個整數(shù)的比較砚亭,比較的代價相對較小灯变,key是泛型,對象的比較比整數(shù)比較
        //代價大捅膘,所以先比較hash添祸,hash相等在比較key
            if (p.hash == hash &&//
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;//e指向一個Node:該Node的key屬性與put時傳入的key參數(shù)相等的那個Node
            else if (p instanceof TreeNode)
               //紅黑樹的插入操作,如果已經存在該key的TreeNode寻仗,則返回該TreeNode刃泌,否則返回null
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //table[i]處存放的是鏈表,接下來和TreeNode類似
                //在遍歷鏈表過程中先判斷是key先前是否存在,如果存在則e指向該Node
                //否則將該Node插入到鏈表末尾耙替,插入后判斷鏈表長度是否>=8亚侠,是的話要進行額外操作
                
                //binCountt最后的值是鏈表的長度
                for (int binCount = 0; ; ++binCount) {
                    
                    if ((e = p.next) == null) {
                   //遍歷到了鏈表最后一個元素,接下來執(zhí)行鏈表的插入操作,先封裝為Node再插入
                   //p指向的是鏈表最后一個節(jié)點俗扇,將待插入的Node置為p.next硝烂,就完成了單鏈表的插入
                        p.next = newNode(hash, key, value, null);

                       
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //TREEIFY_THRESHOLD值是8, binCount>=7,然后又插入了一個新節(jié)點铜幽,            
                            //鏈表長度>=8钢坦,這時要么進行擴容操作,要么把鏈表結構轉為紅黑樹結構
                            //我們接下會分析treeifyBin的源碼實現(xiàn)
                            treeifyBin(tab, hash);
                        break;
                    }
                    
                    //當p不是指向鏈表末尾的時候:先判斷p.key是否等于key啥酱,等于的話表示當前key
                    //已經存在了爹凹,令e指向p,停止遍歷镶殷,最后會更新e的value禾酱;
                    //不等的話準備下次遍歷,令p=p.next绘趋,即p=e
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }

            
            if (e != null) { // existing mapping for key
                //表示當前的key之前已經存在了颤陶,并且上面的邏輯保證:e.key一定等于key
                //這是更新e.value就好
                V oldValue = e.value;//保存oldvalue
                
                //onlyIfAbsent默是false,evict為true
                //onlyIfAbsent為true表示如果之前已經存在key這個鍵值對了陷遮,那么后面在put這個key 
                //時滓走,忽略這個操作,不更新先前的value帽馋,這里連接就好 
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;//更新e.value
                
                //這個函數(shù)的默認實現(xiàn)是“空”搅方,即這個函數(shù)默認什么操作都不執(zhí)行,那為什么要有它呢绽族?
                //這是個hook/鉤子函數(shù)姨涡,主要要在LinkedHashMap中,LinkedHashMap重寫了這個函數(shù)
                //后面講解LinkedHashMap時會詳細講解
                afterNodeAccess(e);
                return oldValue;//返回舊的value
            }
        }

        //如果是第一次插入key這個鍵吧慢,就會執(zhí)行到這里
        ++modCount;//failFast機制
        
        //size保存的是當前HashMap中保存了多少個鍵值對涛漂,HashMap的size方法就是直接返回size
        //之前說過,threshold保存的是當前table數(shù)組長度*loadfactor检诗,如果table數(shù)組中存儲的
        //Node數(shù)量大于threshold匈仗,這時候會進行擴容,即將table數(shù)組的容量翻倍逢慌。后面會詳細講解
        //resize方法
        if (++size > threshold)
            resize();
        
        //這也是一個hook函數(shù)悠轩,作用和afterNodeAccess一樣
        afterNodeInsertion(evict);
        return null;
    }

    //將鏈表轉換為紅黑樹結構,在鏈表的插入操作后調用
/**
     * Replaces all linked nodes in bin at index for given hash unless
     * table is too small, in which case resizes instead.
     */
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        
        //MIN_TREEIFY_CAPACITY值是64,也就是當鏈表長度>8的時候涕癣,有兩種情況:
        //如果table數(shù)組的長度<64,此時進行擴容操作
        //如果table數(shù)組的長度>64哗蜈,此時進行鏈表轉紅黑樹結構的操作
        //具體轉細節(jié)在面試中幾乎沒有問的前标,這里不細講了,
        //大部同學認為鏈表長度>8一定會轉換成紅黑樹距潘,這是不對的A读小!音比!
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

resize方法

該方法在初始化以及put的時候俭尖,需要擴容時這兩種情況下,會執(zhí)行該方法洞翩。

先判斷是否超過最大容量稽犁,此時會把擴容臨界點改為Integer的最大值,正常擴容的話會翻倍骚亿,使用位運算去擴容已亥,并重新計算擴容后的最大容量,擴容臨界值threshold也會*2

如果不擴容来屠,該值為空虑椎,會執(zhí)行初始化。

最后是對擴容時的處理俱笛,會新建一個容量為原來容量2倍的node數(shù)組捆姜,把原數(shù)組同索引下的同一個桶中,不管是紅黑樹還是鏈表結構迎膜,都按照(hash & N(該桶的數(shù)量))得出的值泥技,如果該值為0,則放在鏈表L1(loTail)磕仅,否則L2(hiTail)上.遷移時珊豹,把L1數(shù)據(jù)放到table[i]上面,L2放到table[i+n(擴容前的長度)]上宽涌。

見源碼:

 final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;//保留擴容前數(shù)組引用
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //正常擴容:newCap = oldCap << 1
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                //容量翻倍平夜,擴容后的threshold自然也是*2
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            //table數(shù)組初始化的時候會進入到這里
            newCap = DEFAULT_INITIAL_CAPACITY;//默認容量
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//threshold
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;//更新threshold
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//擴容后的新數(shù)組
        table = newTab;//執(zhí)行容量翻倍的新數(shù)組
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {//之后完成oldTab中Node遷移到table中去
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)//j這個桶位置只有一個元素,直接rehash到table數(shù)組
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                  //如果是紅黑樹:也是將紅黑樹拆分為兩個鏈表卸亮,這里主要看鏈表的拆分,兩者邏輯一樣
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        //鏈表的拆分
                        Node<K,V> loHead = null, loTail = null;//第一個鏈表l1
                        Node<K,V> hiHead = null, hiTail = null;//第二個鏈表l2
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {//rehash到table[j]位置
                            //將當前node連接到l1上
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                 //將當前node連接到l2上
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
    
                        if (loTail != null) {
                            //l1放到table[j]位置
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            //l1放到table[j+oldCap]位置
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

影響HashMap的性能因素(key的hashCode函數(shù)實現(xiàn)玩裙、loadFactor兼贸、初始容量)

key的hashCode函數(shù)實現(xiàn),主要是因為該實現(xiàn)吃溅,可以將key的hashCode的高16位的隨機性與低16位進行異或運算溶诞,增強hash的隨機性減少hash&(n-1)的 隨機性(這個是計算桶的索引的算法),即減小hash沖突决侈,提高HashMap的性能螺垢。
看源碼:

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

loadFactor: 負載因子,決定桶的利用率。當size==桶的數(shù)量DEFAULT_LOAD_FACTOR的時候枉圃,這時HashMap要進行擴容操作功茴,也就是桶不能裝滿。DEFAULT_LOAD_FACTOR是衡量桶的利率:DEFAULT_LOAD_FACTOR較小時(桶的利用率較心跚住),這時浪費的空間較多(因為只能存儲桶的數(shù)量DEFAULT_LOAD_FACTOR個元素坎穿,超過了就要進行擴容),這種情況下往HashMap中put元素時發(fā)生沖突的概率也很小,所謂沖突指的是:多個元素被put到了同一個桶 中; 沖突小時(可以認為一個桶中只有一個元素)put返劲、get等HashMap的操作代價就很低玲昧,可以認為是O(1);桶的利用率較大的時候(DEFAULT_LOAD_FACTOR很大,注意可以大于1篮绿,因為沖突的元素是使用鏈表或者 紅黑樹連接起來的)空間利用率較高

HashMap key的hash值計算方法以及原因(見上面hash函數(shù)的分析)

HashMap內部存儲結構:Node數(shù)組+鏈表或紅黑樹

table[i]位置的鏈表什么時候會轉變成紅黑樹

table[i]位置的鏈表里面的node數(shù)量大于8并且整體容量大于64時孵延,會轉換為紅黑樹存儲結構

HashMap主要成員屬性:threshold(擴容的臨界值)、loadFactor(負載因子)亲配、HashMap的懶加載

HashMap的get方法能否判斷某個元素是否在map中

如果java程序對key不存在和存在但是值為null隙袁,判斷是一致的,那么可以使用該get判斷弃榨,不然還是使用containskey();

HashMap線程安全嗎菩收,哪些環(huán)節(jié)最有可能出問題,為什么鲸睛?

線程不安全娜饵,多線程情況下會導致hashmap鏈表閉環(huán), 一旦進入了閉環(huán)get數(shù)據(jù)官辈,程序就會進入死循環(huán)箱舞,所以導致HashMap是非線程安全的。

HashMap中的hook函數(shù)(可以鏈接到LinkedHashMap講解拳亿,這可作為HashMap的延伸知識晴股,增加面試官對你的印象)

摘自以下博客:
http://www.reibang.com/p/8e668a010f43

線程不安全的hashMap: https://blog.csdn.net/V_Axis/article/details/78604505

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肺魁,隨后出現(xiàn)的幾起案子电湘,更是在濱河造成了極大的恐慌,老刑警劉巖鹅经,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寂呛,死亡現(xiàn)場離奇詭異,居然都是意外死亡瘾晃,警方通過查閱死者的電腦和手機贷痪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹦误,“玉大人劫拢,你說我怎么就攤上這事肉津。” “怎么了舱沧?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵妹沙,是天一觀的道長。 經常有香客問我狗唉,道長初烘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任分俯,我火速辦了婚禮肾筐,結果婚禮上,老公的妹妹穿的比我還像新娘缸剪。我一直安慰自己吗铐,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布杏节。 她就那樣靜靜地躺著唬渗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奋渔。 梳的紋絲不亂的頭發(fā)上镊逝,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音嫉鲸,去河邊找鬼撑蒜。 笑死,一個胖子當著我的面吹牛玄渗,可吹牛的內容都是我干的座菠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼藤树,長吁一口氣:“原來是場噩夢啊……” “哼浴滴!你這毒婦竟也來了?” 一聲冷哼從身側響起岁钓,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤升略,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甜紫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體降宅,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年囚霸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片激才。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡拓型,死狀恐怖额嘿,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情劣挫,我是刑警寧澤册养,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站压固,受9級特大地震影響球拦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜帐我,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一坎炼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拦键,春花似錦谣光、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至媚朦,卻和暖如春氧敢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背询张。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工孙乖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瑞侮。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓的圆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親半火。 傳聞我的和親對象是個殘疾皇子越妈,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容