java.util.HashMap數(shù)組+鏈表源碼(jdk1.7)

準(zhǔn)備知識(shí)

20170614214937227.jpg

HashMap是基于哈希表的非同步的實(shí)現(xiàn),不保證映射的順序永久不變,可以鍵值對(duì)都為null迹缀。哈希表的核心就是根據(jù)元素求位置。而多個(gè)元素可能出現(xiàn)相同的位置蜜徽,那么就叫沖突祝懂。解決沖突的常用方式:鏈地址法:將多個(gè)值的不同的哈希結(jié)果(哈希值)用數(shù)組進(jìn)行存儲(chǔ),然后將產(chǎn)生相同哈希值的元素拘鞋,以單向鏈表的形式進(jìn)行存儲(chǔ)砚蓬。所以拉鏈法的套路就是數(shù)組+單鏈表。第二種解決方式是線性探測(cè)法:將多個(gè)值余上表長(zhǎng)度盆色,如果多個(gè)值產(chǎn)生相同的哈希值灰蛙,那么就依次往下尋找位置祟剔,直到不沖突為止。線性探測(cè)法致命的缺點(diǎn)就是當(dāng)數(shù)據(jù)量上來(lái)時(shí)摩梧,就會(huì)頻繁的進(jìn)行碰撞沖突物延,然后找到位置比較費(fèi)勁。

HashMap中鏈地址的單鏈表結(jié)構(gòu)

    static class Entry<K,V> implements Map.Entry<K,V> {  
            final K key;  // 鍵
            V value;  // 值
            Entry<K,V> next;// 指向下一個(gè)相同hash值得元素  
            int hash;// 哈希值  
      
            Entry(int h, K k, V v, Entry<K,V> n) {  
                value = v;  
                next = n;  
                key = k;  
                hash = h;  
            }  
            .............  
            .............  
    }  

屬性

    //默認(rèn)初始化容量為16  
    static final int DEFAULT_INITIAL_CAPACITY = 16;   

說(shuō)明:HashMap初始化容量為16障本。

    //最大容量教届,必須是2的n次冪  
    static final int MAXIMUM_CAPACITY = 1 << 30;  

說(shuō)明:HashMap的最大容量是2的30次冪。

    //默認(rèn)加載因子  
    static final float DEFAULT_LOAD_FACTOR = 0.75f;  

說(shuō)明:HashMap的默認(rèn)加載因子為0.75驾霜。

    //存儲(chǔ)數(shù)據(jù)的Entry數(shù)組案训,是存儲(chǔ)實(shí)體的數(shù)組,長(zhǎng)度是2的冪  
    transient Entry<K,V>[] table;  

說(shuō)明:HashMap中存儲(chǔ)單向鏈表節(jié)點(diǎn)的數(shù)組粪糙。

    //映射鍵值對(duì)的個(gè)數(shù)强霎,也是數(shù)組中元素的個(gè)數(shù)  
    transient int size;  

說(shuō)明:HashMap中存儲(chǔ)下標(biāo)和節(jié)點(diǎn)的數(shù)組中元素的個(gè)數(shù)。

    //臨界值蓉冈,當(dāng)實(shí)際大小超過(guò)臨界值時(shí)城舞,會(huì)進(jìn)行擴(kuò)容threshold = 加載因子*容量  
    int threshold;  

說(shuō)明:HashMap中的臨界值,如果實(shí)際的大小超出了臨界值會(huì)進(jìn)行擴(kuò)容寞酿。

    //哈希表的加載因子  
    final float loadFactor;  

說(shuō)明:HashMap中的加載因子家夺。

    //被修改的次數(shù)  
    transient int modCount; 

說(shuō)明:HashMap中被修改的次數(shù)。

構(gòu)造方法

構(gòu)造方法1:初始化一個(gè)Entry數(shù)組伐弹,數(shù)組大小為2的n次冪

        public HashMap(int initialCapacity, float loadFactor) {  
            //如果初始化容量小于0拉馋,報(bào)非法參數(shù)異常  
            if (initialCapacity < 0)  
                throw new IllegalArgumentException("Illegal initial capacity: " +  
                                                   initialCapacity);  
            //如果初始化容量大于最大容量,那么將最大容量作為初始化容量  
            if (initialCapacity > MAXIMUM_CAPACITY)  
                initialCapacity = MAXIMUM_CAPACITY;  
            //如果加載因子小于0或者為非小數(shù)惨好,那么就報(bào)非法參數(shù)異常  
            if (loadFactor <= 0 || Float.isNaN(loadFactor))  
                throw new IllegalArgumentException("Illegal load factor: " +  
                                                   loadFactor);  
      
            // 讓容量以2的n次冪遞增煌茴,這樣保證了容量大小為偶數(shù)  
            int capacity = 1;  
            while (capacity < initialCapacity)  
                capacity <<= 1;  
      
            this.loadFactor = loadFactor;  
            threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);  
            //以定義的容量capacity初始化Entry數(shù)組  
            table = new Entry[capacity];  
            useAltHashing = sun.misc.VM.isBooted() &&  
                    (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);  
            init();  
        }  

說(shuō)明:容量按照2的n次冪遞增,保證容量為偶數(shù)日川。
因?yàn)橹付ǖ某跏蓟萘亢拓?fù)載因子蔓腐,所以要嚴(yán)格設(shè)計(jì)。計(jì)算出的臨界值如果過(guò)小龄句,那么后面元素上來(lái)需要擴(kuò)容回论。


構(gòu)造方法2:使用默認(rèn)的加載因子和初始化容量構(gòu)造HashMap

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

構(gòu)造方法3:使用默認(rèn)的初始化容量16,和默認(rèn)的加載因子0.75構(gòu)造HashMap

        public HashMap() {  
            this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);  
        }  

構(gòu)造方法4:構(gòu)造一個(gè)映射關(guān)系與指定 Map 相同的 HashMap分歇;所創(chuàng)建的 HashMap 具有默認(rèn)的加載因子 (0.75) 和足以容納指定 Map中映射關(guān)系的初始容量透葛。

        public HashMap(Map<? extends K, ? extends V> m) {  
            this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,  
                          DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);  
            putAllForCreate(m);  
        }  
      
        //遍歷m獲取值,放入哈希表  
        private void putAllForCreate(Map<? extends K, ? extends V> m) {  
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet())  
                putForCreate(e.getKey(), e.getValue());  
        }  

        private void putForCreate(K key, V value) {  
            //如果鍵為空卿樱,hash值為0僚害,如果鍵不為空,那么就通過(guò)鍵來(lái)生成hash值  
            int hash = null == key ? 0 : hash(key);  
            //通過(guò)hash值來(lái)獲取索引  
            int i = indexFor(hash, table.length);  
      
            /**  
             * 通過(guò)索引查找到table中的Entry節(jié)點(diǎn),進(jìn)行遍歷單鏈表中的Entry節(jié)點(diǎn)萨蚕。  
             * 如果出現(xiàn)了該索引對(duì)應(yīng)hash值和鍵都相同的靶草,那么就直接退出該方法。  
             */  
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
                Object k;  
                if (e.hash == hash &&  
                    ((k = e.key) == key || (key != null && key.equals(k)))) {  
                    e.value = value;  
                    return;  
                }  
            }  
            //否則就執(zhí)行createEntry方法創(chuàng)建該桶  
            createEntry(hash, key, value, i);  
        }  

關(guān)鍵方法設(shè)計(jì)

1. hash方法:使用鍵key生成hash值

通過(guò)鍵來(lái)獲取對(duì)應(yīng)的hash值岳遥。使用hashCode方法先散列一次奕翔,然后進(jìn)行高位右移、異或運(yùn)算浩蓉,在進(jìn)行低位右移派继、異或運(yùn)算

        // 根據(jù)鍵來(lái)獲取對(duì)應(yīng)的hash值  
    final int hash(Object k) {  
        int h = 0;  
        if (useAltHashing) {  
            if (k instanceof String) {  
                return sun.misc.Hashing.stringHash32((String) k);  
            }  
            h = hashSeed;  
        }  
              
        h ^= k.hashCode();  
      
        h ^= (h >>> 20) ^ (h >>> 12);  
        return h ^ (h >>> 7) ^ (h >>> 4);  
    }  

2. indexFor方法:使用hash值和鍵表的長(zhǎng)度生成鍵表的下標(biāo)(鍵表:Entry<K,V>[] table)

        // 返回hash值對(duì)應(yīng)的下標(biāo)
    static int indexFor(int h, int length) {  
        return h & (length-1);  
    }  

說(shuō)明:
在我們自己實(shí)現(xiàn)哈希表的時(shí)候,常常會(huì)采用取%的方式來(lái)進(jìn)行散列捻艳,將余數(shù)作為散列碼驾窟。但是java采用的是二進(jìn)制與運(yùn)算來(lái)取hash值。由于length為2的n次冪认轨,所以其減一為奇數(shù)绅络,那么轉(zhuǎn)化為二進(jìn)制,末位肯定是1嘁字。如果hash值h為奇數(shù)恩急,那么h與length的與運(yùn)算,返回索引的二進(jìn)制末位為1.如果hash值h為偶數(shù)纪蜒,那么h與length的與運(yùn)算衷恭,返回索引的二進(jìn)制末位為0。這就相當(dāng)于取模運(yùn)算了纯续,但是與位運(yùn)算比取模速度快很多随珠。并且這樣也可以保證數(shù)組的下標(biāo)不會(huì)越界。是一個(gè)很棒的選擇杆烁。由于與運(yùn)算加上前面hash(key)函數(shù)的高位運(yùn)算,就減少了碰撞简卧。這樣就保證了hash值分布比較均勻并且沖突的概率很低兔魂。并且只有在值相同時(shí),才會(huì)對(duì)應(yīng)數(shù)組中的相同位置Entry举娩,然后以next的關(guān)系構(gòu)成鏈表析校。

3. createEntry方法:初始化Entry。如果沒(méi)有相同的hash值和鍵铜涉,就會(huì)使用createEntry來(lái)創(chuàng)建單鏈表智玻。

    void createEntry(int hash, K key, V value, int bucketIndex) {  
            //根據(jù)索引得出對(duì)應(yīng)定義的Entry類型對(duì)象e  
            Entry<K,V> e = table[bucketIndex];  
            //將e進(jìn)行初始化哈希值、鍵芙代、值吊奢,以及單鏈表指向的下一個(gè)節(jié)點(diǎn)e  
            table[bucketIndex] = new Entry<>(hash, key, value, e);  
            size++;  
    }  

常用方法

get方法:返回指定鍵所映射的值;如果對(duì)于該鍵來(lái)說(shuō)纹烹,此映射不包含任何映射關(guān)系页滚,則返回 null

public V get(Object key) {
    // 如果鍵為null
    if (key == null)  
        // 返回獲取null鍵對(duì)應(yīng)的值
        return getForNullKey(); 
    // 獲取鍵對(duì)應(yīng)的值
    Entry<K,V> entry = getEntry(key);  
    // 如果獲取的節(jié)點(diǎn)為null召边,那么返回null;否則獲取節(jié)點(diǎn)對(duì)應(yīng)的值
    return null == entry ? null : entry.getValue();  
}

getForNullKey方法:查找null鍵對(duì)應(yīng)的值

// 獲取null鍵的值
private V getForNullKey() {
    // 如果鍵表中沒(méi)有元素裹驰,返回null
    if (size == 0) {
        return null;
    }
    // 從鍵表的第一個(gè)節(jié)點(diǎn)開(kāi)始隧熙,循環(huán)遍歷存放節(jié)點(diǎn)的單鏈表
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
        // 如果存在鍵值等于null的節(jié)點(diǎn)
        if (e.key == null)
            // 返回節(jié)點(diǎn)的值
            return e.value;
    }
    return null;
}

getEntry方法:查找鍵對(duì)應(yīng)的Entry節(jié)點(diǎn)

final Entry<K,V> getEntry(Object key) {  
    // 如果鍵為null,返回hash值為0
    // 如果鍵不為null幻林,返回計(jì)算的hash值
    int hash = (key == null) ? 0 : hash(key);  
    // 使用hash值計(jì)算鍵表下標(biāo)贞盯,找到對(duì)應(yīng)的單向節(jié)點(diǎn)鏈表
    // 遍歷節(jié)點(diǎn)鏈
    for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {  
        Object k;  
        // 判斷遍歷節(jié)點(diǎn)的hash值和鍵是否等于查找的鍵和hash值
        if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))  
            return e;  
    }  
    return null;  
}  

說(shuō)明:
① 當(dāng)鍵為空,就遍歷單向節(jié)點(diǎn)鏈中的節(jié)點(diǎn)沪饺,遍歷到的節(jié)點(diǎn)的鍵為null躏敢,就返回節(jié)點(diǎn)對(duì)應(yīng)的值。
② 當(dāng)鍵不為空随闽,那么就通過(guò)鍵獲取hash值父丰,再通過(guò)hash值獲取鍵表的下標(biāo),通過(guò)下標(biāo)索引獲取對(duì)應(yīng)的Entry單向鏈表掘宪。遍歷單鏈表找到hash值和key鍵都相同的值蛾扇。


put方法:在此映射中關(guān)聯(lián)指定值與指定鍵。如果該映射以前包含了一個(gè)該鍵的映射關(guān)系魏滚,則舊值被替換镀首。

public V put(K key, V value) {  
    // 如果鍵為null
    if (key == null)  
        //設(shè)置鍵key為null的值 
        return putForNullKey(value);  
    //通過(guò)鍵獲取hash值  
    int hash = hash(key);  
    //通過(guò)hash值獲取下標(biāo)索引  
    int i = indexFor(hash, table.length);  
    // 遍歷單鏈表節(jié)點(diǎn),找到對(duì)應(yīng)的值就覆蓋
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
        Object k;  
        // 如果hash值和鍵都相同的
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
            // 獲取舊的鍵對(duì)應(yīng)的值
            V oldValue = e.value; 
            // 設(shè)置指定的值
            e.value = value;  
            e.recordAccess(this);  
            // 返回舊值
            return oldValue;  
        }  
    }  
    // 修改次數(shù)加1
    modCount++;  
    //否則就添加新值并且返回為空  
    addEntry(hash, key, value, i);  
    return null;  
}

putForNullKey方法:設(shè)置鍵key為null的值

private V putForNullKey(V value) {  
    // 鍵表0下標(biāo)開(kāi)始遍歷
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {  
        // 如果鍵為null
        if (e.key == null) {  
            // 獲取舊的鍵對(duì)應(yīng)的值
            V oldValue = e.value; 
            // 設(shè)置指定的值
            e.value = value;  
            e.recordAccess(this);  
            return oldValue;  
       }  
   }  
   // 修改次數(shù)加1
   modCount++; 
   // 添加新的值
   addEntry(0, null, value, 0);  
   return null;  
}

addEntry方法:添加新的值

void addEntry(int hash, K key, V value, int bucketIndex) {   
    // 如果鍵表超出了臨界值并且下標(biāo)對(duì)應(yīng)的值不為null
    if ((size >= threshold) && (null != table[bucketIndex])) {  
        // 將HashMap擴(kuò)容
        resize(2 * table.length); 
        // 如果鍵key不為null就獲取對(duì)應(yīng)的hash值鼠次,否則獲取hash值為0
        hash = (null != key) ? hash(key) : 0;  
        // 通過(guò)hash值計(jì)算鍵表下標(biāo)
        bucketIndex = indexFor(hash, table.length);  
    }  
    //通過(guò)新的hash值更哄、鍵、值腥寇、下標(biāo)索引來(lái)以構(gòu)造器的方式創(chuàng)建Entry單向鏈表 
    createEntry(hash, key, value, bucketIndex);  
}

resize方法:使用新容量進(jìn)行擴(kuò)容操作

void resize(int newCapacity) {  
    // 獲取舊表
    Entry[] oldTable = table;
    // 獲取舊表的容量
    int oldCapacity = oldTable.length;  
    //如果舊的容量值都已經(jīng)達(dá)到最大容量成翩,那么就不再進(jìn)行擴(kuò)容調(diào)整了,直接退出了  
    if (oldCapacity == MAXIMUM_CAPACITY) {  
        // 設(shè)置臨界值為Integer.MAX_VALUE
        threshold = Integer.MAX_VALUE;  
        return;  
    }  
    //根據(jù)新容量長(zhǎng)度定義新的數(shù)組  
    Entry[] newTable = new Entry[newCapacity];  
    boolean oldAltHashing = useAltHashing;  
    useAltHashing |= sun.misc.VM.isBooted() &&  
            (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);  
    boolean rehash = oldAltHashing ^ useAltHashing;  
    //定義了新容量的數(shù)組赦役,那么這步就是將舊容量數(shù)組中的元素都放到新容量的數(shù)組中去  
    transfer(newTable, rehash);  
    //更新當(dāng)前的鍵表  
    table = newTable;  
    //臨界值由新容量進(jìn)行調(diào)整  
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);  
} 

transfer方法:將舊表中的元素都轉(zhuǎn)移到新表中

void transfer(Entry[] newTable, boolean rehash) {  
    // 獲取新表的容量  
    int newCapacity = newTable.length;  
    // 遍歷舊鍵表中的Entry麻敌,然后通過(guò)舊鍵表中的Entry創(chuàng)建新鍵表的Entry  
    for (Entry<K,V> e : table) {
        // 如果節(jié)點(diǎn)Entry不為null
        while(null != e) { 
            // 獲取遍歷到節(jié)點(diǎn)的后繼節(jié)點(diǎn)
            Entry<K,V> next = e.next;  
            // 如果需要就重新通過(guò)鍵生成hash值  
            if (rehash) {  
                e.hash = null == e.key ? 0 : hash(e.key);  
            }  
            // 通過(guò)hash值獲取下標(biāo)索引  
            int i = indexFor(e.hash, newCapacity);  
            // 設(shè)置當(dāng)前Entry節(jié)點(diǎn)e指向的下一個(gè)節(jié)點(diǎn)Entry值為newTable[i]  
            // 將舊的鍵位置對(duì)應(yīng)的值作為當(dāng)前Entry的后繼值
            e.next = newTable[i];  
            // 更新當(dāng)前鍵數(shù)組的值為最新值  
            newTable[i] = e;  
            // 向下個(gè)節(jié)點(diǎn)遍歷  
            e = next;  
        }  
    }  
}

說(shuō)明:
可以看到為了轉(zhuǎn)移元素,使用舊的值生成hash值掂摔,再重新計(jì)算下標(biāo)索引的位置术羔,用了兩層循環(huán),時(shí)間復(fù)雜度增加了數(shù)量級(jí)乙漓,非常消耗性能级历。所以如果我們已知了需要的空間,那么就會(huì)減少不必要的因?yàn)槌龅脑虍a(chǎn)生的擴(kuò)容操作叭披,減少了性能的消耗寥殖。


總結(jié)

1. HashMap的設(shè)計(jì)采用數(shù)組+單向鏈表實(shí)現(xiàn)的。
2. HashMap的默認(rèn)初始化容量為16 ,默認(rèn)加載因子為0.75扛禽。
3. HashMap指定初始容量:按照2的n次冪計(jì)算锋边。
4. HashMap中鍵和值都允許存入null值。
5. HashMap中值允許重復(fù)编曼,如果發(fā)現(xiàn)鍵相同豆巨,就更新原來(lái)的值。
6. HashMap是線程不安全的掐场。
7. HashMap是無(wú)序的往扔,不是按照插入順序遍歷出來(lái)的。
8. hash函數(shù)熊户,通過(guò)鍵獲取hash值萍膛,分別使用高位和低位的右移、異或運(yùn)算減少?zèng)_突嚷堡。
9. indexFor函數(shù)蝗罗,通過(guò)hash值獲取索引,使用與位運(yùn)算代替取模運(yùn)算蝌戒,速度就提高了串塑,而且也不用擔(dān)心下標(biāo)越界的問(wèn)題了。
10. put函數(shù)北苟,臨界值等于鍵表容量乘以負(fù)載因子桩匪,當(dāng)我們添加的時(shí)候會(huì)計(jì)算鍵表的長(zhǎng)度是否大于臨界值,如果我們初始化時(shí)臨界值過(guò)小友鼻,那么我們就需要擴(kuò)容傻昙,這樣操作會(huì)帶來(lái)性能消耗。
11. put函數(shù)彩扔,如果大于臨界值將會(huì)擴(kuò)容妆档,擴(kuò)容策略,一次擴(kuò)容為原來(lái)的2倍虫碉。
12. 為什么數(shù)組的長(zhǎng)度為2的n次冪:減小哈希沖突概率贾惦,而且還可以防止數(shù)組下標(biāo)越界。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔗衡,一起剝皮案震驚了整個(gè)濱河市纤虽,隨后出現(xiàn)的幾起案子乳绕,更是在濱河造成了極大的恐慌绞惦,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洋措,死亡現(xiàn)場(chǎng)離奇詭異济蝉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門王滤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贺嫂,“玉大人,你說(shuō)我怎么就攤上這事雁乡〉谠” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵踱稍,是天一觀的道長(zhǎng)曲饱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)珠月,這世上最難降的妖魔是什么扩淀? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮啤挎,結(jié)果婚禮上驻谆,老公的妹妹穿的比我還像新娘。我一直安慰自己庆聘,他們只是感情好胜臊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著掏觉,像睡著了一般区端。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上澳腹,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天织盼,我揣著相機(jī)與錄音,去河邊找鬼酱塔。 笑死沥邻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羊娃。 我是一名探鬼主播唐全,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蕊玷!你這毒婦竟也來(lái)了邮利?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤垃帅,失蹤者是張志新(化名)和其女友劉穎延届,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贸诚,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡方庭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年厕吉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片械念。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡头朱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出龄减,到底是詐尸還是另有隱情项钮,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布希停,位于F島的核電站寄纵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏脖苏。R本人自食惡果不足惜程拭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棍潘。 院中可真熱鬧恃鞋,春花似錦、人聲如沸亦歉。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肴楷。三九已至水由,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赛蔫,已是汗流浹背砂客。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呵恢,地道東北人鞠值。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像渗钉,于是被迫代替她去往敵國(guó)和親彤恶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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