Java1.8-PriorityQueue源碼解析

概述

??首先逢并,Priority翻譯之后是優(yōu)先級(jí)的意思之剧,而PriorityQueue,也就是優(yōu)先級(jí)隊(duì)列的意思砍聊。優(yōu)先級(jí)隊(duì)列背稼,和普通的先進(jìn)先出(FIFO)的隊(duì)列不同,優(yōu)先隊(duì)列每次取出的元素都是隊(duì)列中優(yōu)先級(jí)最高的玻蝌,而PriorityQueue默認(rèn)優(yōu)先級(jí)是取出元素最小的值蟹肘,當(dāng)然也可以按照我們指定的規(guī)則來(lái)自定義優(yōu)先級(jí)。
首先俯树,我們先看下官方API:https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html
通過(guò)文檔帘腹,我們可以看到PriorityQueue優(yōu)先級(jí)隊(duì)列的一些性質(zhì):

  1. PriorityQueue是基于優(yōu)先級(jí)堆來(lái)實(shí)現(xiàn)的無(wú)界優(yōu)先隊(duì)列,是在JDK1.5之后引入的類阳欲;
  2. 優(yōu)先隊(duì)列的元素按照它們的自然排序順序排列,或者根據(jù)使用的構(gòu)造方法提供的比較器來(lái)進(jìn)行排序陋率;
  3. 優(yōu)先隊(duì)列不允許null元素球化;
  4. 優(yōu)先隊(duì)列不允許插入非可比的對(duì)象,有可能會(huì)拋出ClassCastException瓦糟,比如對(duì)int排序筒愚,你添加了一個(gè)字符串類型的元素。
  5. 該優(yōu)先隊(duì)列每次取出的元素都是最小的菩浙。接口offer, poll, remove(),add方法巢掺,查詢時(shí)間都是O(log(n)),
  6. 優(yōu)先隊(duì)列迭代器迭代的順序無(wú)法保證劲蜻,如果想有序遍歷陆淀,請(qǐng)考慮使用Arrays.sort(priorityQueue.toArray());而對(duì)于remove(Object) and contains(Object)方法斋竞,查詢是線性時(shí)間倔约;而對(duì)于peek, element, and size方法,查詢是常數(shù)時(shí)間坝初。
  7. 該優(yōu)先隊(duì)列不是線程安全的浸剩,如果要在線程安全的場(chǎng)景下使用钾军,建議使用線程安全的PriorityBlockingQueue類(優(yōu)先級(jí)阻塞隊(duì)列);

??PriorityQueue是基于堆來(lái)實(shí)現(xiàn)的绢要,而堆這個(gè)數(shù)據(jù)結(jié)構(gòu)是怎樣的呢吏恭。我們來(lái)大概說(shuō)下,學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)的應(yīng)該都知道重罪,堆是利用完全二叉樹(shù)來(lái)進(jìn)行處理數(shù)據(jù)樱哼,而完全二叉樹(shù)又是怎樣的呢。

??若二叉樹(shù)的深度是h剿配,那么除第h層外搅幅,其他各層(1~h-1)層的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù),第h層所有的結(jié)點(diǎn)都連續(xù)集中在最左邊呼胚,這就是完全二叉樹(shù)茄唐。而堆就是基于這種數(shù)據(jù)結(jié)構(gòu)來(lái)處理數(shù)據(jù)的。我們用一張圖大致來(lái)看下完全二叉樹(shù):


完全二叉樹(shù)圖示

??如圖所示蝇更,圖1是完全二叉樹(shù)沪编,而圖二由于第三層所有的節(jié)點(diǎn)并非連續(xù)集中在最左側(cè),所以不是完全二叉樹(shù)年扩;而圖三蚁廓,由于第三層的結(jié)點(diǎn)沒(méi)有達(dá)到最大個(gè)數(shù)(最大個(gè)數(shù)是4),所以也不是完全二叉樹(shù)厨幻。

其中堆一般還分為兩種:最大堆和最小堆相嵌。

  1. 顧名思義,最小堆就是根結(jié)點(diǎn)是所有數(shù)據(jù)中最小的元素克胳,并且堆中每個(gè)結(jié)點(diǎn)的值總是不大于其孩子結(jié)點(diǎn)的值平绩。
  2. 而最大堆就是根結(jié)點(diǎn)是所有結(jié)點(diǎn)中最大的元素,并且堆中每個(gè)結(jié)點(diǎn)的值總是不小于其孩子結(jié)點(diǎn)的值漠另。

??而PriorityQueue則是基于最小堆來(lái)實(shí)現(xiàn)的,并且底層是通過(guò)數(shù)組來(lái)構(gòu)建堆的數(shù)據(jù)結(jié)構(gòu)跃赚。由于本文的重點(diǎn)并不是堆亦或是完全二叉樹(shù)笆搓,所以堆的介紹就到這里來(lái)了。接下來(lái)我們來(lái)學(xué)習(xí)PriorityQueue的源碼纬傲。

繼承結(jié)構(gòu)及屬性

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {
    
    // 使用數(shù)組來(lái)構(gòu)造堆满败,也就是優(yōu)先級(jí)隊(duì)列
    transient Object[] queue;
    // 數(shù)組默認(rèn)的初始化容量
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 隊(duì)列的容量
    private int size = 0;
    // 比較器
    private final Comparator<? super E> comparator;
    // 結(jié)構(gòu)化修改次數(shù)
    transient int modCount = 0;
    // 數(shù)組最大容量限制,Integer最大值-8是說(shuō)一些虛擬機(jī)可能會(huì)在數(shù)組中保留一些header words的空間,
    // 所以沒(méi)有取Integer最大值
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

首先,PriorityQueue是繼承自AbstractQueue肤京,AbstractQueue則是實(shí)現(xiàn)了Queue接口稠茂,封裝了一些隊(duì)列基礎(chǔ)的方法或链。

  1. 優(yōu)先隊(duì)列底層使用數(shù)組來(lái)操作數(shù)據(jù)隙畜,并且數(shù)組的默認(rèn)初始化容量是11蒜埋,如果我們沒(méi)有指定比較器玖详,那系統(tǒng)將按照自然順序來(lái)構(gòu)建優(yōu)先隊(duì)列挖藏。
  2. 根據(jù)數(shù)組queue的注釋暑刃,我們可以知道,父節(jié)點(diǎn)與子節(jié)點(diǎn)的下標(biāo)值之間的關(guān)聯(lián)關(guān)系是:childLeftNode= 2 * parentNode + 1; childRightNode= 2 * (parentNode + 1);膜眠,那么根據(jù)當(dāng)前結(jié)點(diǎn)計(jì)算父結(jié)點(diǎn)也很簡(jiǎn)單:parentNode = (thisNode - 1) / 2岩臣。

??Priority queue represented as a balanced binary heap: the two children of queue[n] are queue[2n+1] and queue[2(n+1)]. The priority queue is ordered by comparator, or by the elements natural ordering, if comparator is null: For each node n in the heap and each descendant d of n, n <= d. The element with the lowest value is in queue[0], assuming the queue is nonempty.

比如依次調(diào)用PriorityQueue的add方法,放入:3宵膨,5架谎,7,1辟躏,9狐树,12,18鸿脓,7抑钟,6。畫(huà)了一張圖野哭,大概如下:


優(yōu)先隊(duì)列數(shù)據(jù)

構(gòu)造方法

了解了優(yōu)先隊(duì)列的大致結(jié)構(gòu)及父子間關(guān)聯(lián)關(guān)系之后在塔,我們來(lái)看幾個(gè)常用的構(gòu)造方法。

/**
 * 默認(rèn)構(gòu)造方法拨黔,初始化容量為11蛔溃,比較器為空
 */
public PriorityQueue() {
    this(DEFAULT_INITIAL_CAPACITY, null);
}

/**
 * 指定具體容量的構(gòu)造方法
 */
public PriorityQueue(int initialCapacity) {
    this(initialCapacity, null);
}

/**
 * 指定比較器的構(gòu)造方法
 */
public PriorityQueue(Comparator<? super E> comparator) {
    this(DEFAULT_INITIAL_CAPACITY, comparator);
}

/**
 * 指定容量和比較器的構(gòu)造方法,
 */
public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
    // Note: This restriction of at least one is not actually needed,
    // but continues for 1.5 compatibility
    if (initialCapacity < 1)
        throw new IllegalArgumentException();
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
}

/**
 * 將集合轉(zhuǎn)換為隊(duì)列篱蝇,根據(jù)集合類型不同贺待,又分為三種情況
 */
public PriorityQueue(Collection<? extends E> c) {
    if (c instanceof SortedSet<?>) {
        SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
        this.comparator = (Comparator<? super E>) ss.comparator();
        initElementsFromCollection(ss);
    }
    else if (c instanceof PriorityQueue<?>) {
        PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
        this.comparator = (Comparator<? super E>) pq.comparator();
        initFromPriorityQueue(pq);
    }
    else {
        this.comparator = null;
        initFromCollection(c);
    }
}

還有幾個(gè)構(gòu)造方法是是實(shí)現(xiàn)方式和上面這幾個(gè)差不多,就不一一貼上了零截。

方法
add方法
public boolean add(E e) {
    return offer(e);
}

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    // 是否擴(kuò)容
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);
    return true;
}

首先麸塞,add方法內(nèi)部全是通過(guò)offer方法來(lái)實(shí)現(xiàn)的,我們來(lái)看一下offer方法的大致實(shí)現(xiàn)過(guò)程:

  1. 判空涧衙,添加數(shù)據(jù)不能為null哪工;
  2. 根據(jù)size與數(shù)組長(zhǎng)度比較,判斷是否擴(kuò)容弧哎;
  3. 判斷添加的是不是第一個(gè)元素雁比,如果不是,進(jìn)行調(diào)整撤嫩,增加完元素之后偎捎,我們需要進(jìn)行調(diào)整才能維護(hù)最大堆或最小堆的性質(zhì)。而這里的這個(gè)調(diào)整在堆中被稱為上浮(對(duì)應(yīng)的方法是siftUp)茴她。
grow方法

那么我們就接著來(lái)看擴(kuò)容方法grow寻拂。

private void grow(int minCapacity) {
    int oldCapacity = queue.length;
    // Double size if small; else grow by 50%
    int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                     (oldCapacity + 2) :
                                     (oldCapacity >> 1));
    // overflow-conscious code
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    queue = Arrays.copyOf(queue, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

擴(kuò)容的時(shí)候,先判斷當(dāng)前隊(duì)列容量是否小于64败京,如果是擴(kuò)容一倍容量兜喻,如果不是,擴(kuò)容原容量的1/2赡麦。而hugeCapacity方法我們也看到多次了朴皆,參考List的擴(kuò)容就知道了,就是溢出檢查泛粹,而拷貝的方法也還是Arrays.copyOf方法遂铡。

siftUp方法

這個(gè)方法就是添加完元素之后進(jìn)行的上浮調(diào)整方法。

private void siftUp(int k, E x) {
    // 是否有比較器
    if (comparator != null)
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

/**
 * 如果有比較器晶姊,調(diào)用該方法扒接,內(nèi)部實(shí)現(xiàn)和siftUpComparable類似
 */
private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

/**
 * 如果沒(méi)有比較器,調(diào)用該方法
 */
private void siftUpComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    // k是下標(biāo)值们衙,保證結(jié)點(diǎn)有父級(jí)結(jié)點(diǎn)
    while (k > 0) {
        // 通過(guò)無(wú)符號(hào)右移钾怔,計(jì)算父級(jí)結(jié)點(diǎn)下標(biāo),-> parent = (thisNode -1) / 2
        int parent = (k - 1) >>> 1;
        // 獲取父級(jí)結(jié)點(diǎn)的值
        Object e = queue[parent];
        // 比較要插入結(jié)點(diǎn)與父級(jí)結(jié)點(diǎn)值蒙挑,如果大于父級(jí)結(jié)點(diǎn)宗侦,不需要移動(dòng),結(jié)束
        if (key.compareTo((E) e) >= 0)
            break;
        // 如果小于父結(jié)點(diǎn)值忆蚀,父結(jié)點(diǎn)值下沉
        queue[k] = e;
        // 改變下標(biāo)值
        k = parent;
    }
    // 最終將當(dāng)前結(jié)點(diǎn)值放到對(duì)應(yīng)的位置上
    queue[k] = key;
}

通過(guò)代碼我們可以看到矾利,調(diào)整的方式是判斷是否有比較器,然后調(diào)用各自對(duì)應(yīng)的處理方法馋袜。

  1. 沒(méi)有比較器男旗,會(huì)按照我們添加的數(shù)據(jù)類型的比較器,也就是所謂的自然順序來(lái)進(jìn)行比較欣鳖。比如說(shuō)察皇,我們添加的數(shù)據(jù)類型是int,那么這里會(huì)按照Integer的Comparable來(lái)進(jìn)行排序观堂;
  2. 有比較器让网,按照對(duì)應(yīng)的比較器的順序來(lái)進(jìn)行上浮操作。

由于畫(huà)圖工具太繁瑣师痕,操作流程可以參考網(wǎng)上的一個(gè)示例:


上浮操作

圖片來(lái)源于:JDK源碼研究PriorityQueue(優(yōu)先隊(duì)列)

peek方法和poll方法
public E peek() {
    return (size == 0) ? null : (E) queue[0];
}

public E poll() {
    if (size == 0)
        return null;
    int s = --size;
    modCount++;
    // 堆中最小值
    E result = (E) queue[0];
    // 取數(shù)組中最后一個(gè)元素的值
    E x = (E) queue[s];
    // 將堆中最后一個(gè)值設(shè)置為null
    queue[s] = null;
    // 如果數(shù)組不是只有一個(gè)元素,執(zhí)行下沉操作
    if (s != 0)
        // 下沉操作
        siftDown(0, x);
    return result;
}

??這兩個(gè)都是出隊(duì)的方法而账。其中胰坟,peek方法特別簡(jiǎn)單,就是取出堆中最小值元素,然后不移除數(shù)據(jù)笔横,不用調(diào)整堆竞滓。
??而poll方法是取出堆中最小值的同時(shí),移除該數(shù)據(jù)吹缔,同樣商佑,為了保證最小堆的性質(zhì),我們需要對(duì)堆進(jìn)行另一種操作:下沉(siftDown)厢塘。

siftDown方法
private void siftDown(int k, E x) {
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}

private void siftDownComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>)x;
    // 計(jì)算非葉子結(jié)點(diǎn)元素的最大位置茶没,循環(huán)的終止條件(在最后一個(gè)非葉子節(jié)點(diǎn)處結(jié)束)
    int half = size >>> 1;        // loop while a non-leaf
    while (k < half) {
        // 計(jì)算k位置處的左子結(jié)點(diǎn)
        int child = (k << 1) + 1; // assume left child is least
        Object c = queue[child];
        // 右子結(jié)點(diǎn)等于左子結(jié)點(diǎn)下標(biāo)加1
        int right = child + 1;
        // 獲取左右孩子中值較小的值 
        if (right < size &&
            ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
            c = queue[child = right];
        // 然后重新和父結(jié)點(diǎn)進(jìn)行比較,如果大于父結(jié)點(diǎn)晚碾,不需要移動(dòng)抓半,結(jié)束
        if (key.compareTo((E) c) <= 0)
            break;
        // 父結(jié)點(diǎn)下移
        queue[k] = c;
        // 改變下標(biāo),循環(huán)此操作
        k = child;
    }
    queue[k] = key;
}
  1. 先計(jì)算非葉子結(jié)點(diǎn)元素的最大位置格嘁;
  2. 循環(huán)判斷是否是葉子結(jié)點(diǎn)笛求;
  3. 計(jì)算根結(jié)點(diǎn)的左右子結(jié)點(diǎn)(也可以邏輯理解為將尾結(jié)點(diǎn)移動(dòng)至根結(jié)點(diǎn)位置),然后循環(huán)比較根結(jié)點(diǎn)的左右子結(jié)點(diǎn)與尾結(jié)點(diǎn)值大小糕簿,直到尾結(jié)點(diǎn)的值小于其左右結(jié)點(diǎn)探入,或者尾結(jié)點(diǎn)處于葉子結(jié)點(diǎn)位置上時(shí)循環(huán)終止。

由于siftDownUsingComparator方法內(nèi)部和siftDownComparable類似懂诗,就不解釋了蜂嗽。


下沉操作

??當(dāng)最小元素14出隊(duì),從數(shù)組尾處取39賦值給隊(duì)首响禽。之后徒爹,進(jìn)行和增加元素后相反的動(dòng)作即下沉。
??首先選出根節(jié)點(diǎn)(父節(jié)點(diǎn))39的兩個(gè)孩子結(jié)點(diǎn)中較小者芋类,和39交換位置隆嗅;當(dāng)39找到新位置后,執(zhí)行同種方法侯繁,如果孩子結(jié)點(diǎn)為null或者都比39大胖喳,則結(jié)束。

remove方法
public boolean remove(Object o) {
    //獲取元素的下標(biāo)    
    int i = indexOf(o);
    if (i == -1)
        return false;
    else {
        removeAt(i);
        return true;
    }
}

/**
 * 執(zhí)行移除操作
 */
private E removeAt(int i) {
    // assert i >= 0 && i < size;
    modCount++;
    int s = --size;
    // 如果要移除的就是最后一個(gè)元素贮竟,賦值為null
    if (s == i) // removed last element
        queue[i] = null;
    else {
        // 取隊(duì)列尾元素
        E moved = (E) queue[s];
        // 將隊(duì)尾元素置為null
        queue[s] = null;
        // 下沉操作
        siftDown(i, moved);
        // 如果下移后元素位置沒(méi)發(fā)生變化丽焊,說(shuō)明moved的左右子結(jié)點(diǎn)都大于moved,這時(shí)就需要上浮操作
        if (queue[i] == moved) {
            // 上浮操作
            siftUp(i, moved);
            // 如果上浮之后發(fā)生了元素位置
            if (queue[i] != moved)
                return moved;
        }
    }
    return null;
}

通過(guò)代碼咕别,我們可以知道remove方法是從隊(duì)列中移除指定元素技健,如果該元素有不止一個(gè),則移除數(shù)組中的一個(gè)出現(xiàn)的元素惰拱。


下沉操作
上浮操作
其他方法
/**
 * 查看是否包含雌贱,只需要拿indexOf的返回值和1進(jìn)行比較即可
 */
public boolean contains(Object o) {
    return indexOf(o) != -1;
}

/**
 * 獲取元素的下標(biāo)
 */
private int indexOf(Object o) {
    if (o != null) {
        // 循環(huán)遍歷
        for (int i = 0; i < size; i++)
            // 調(diào)用equals方法進(jìn)行比較
            if (o.equals(queue[i]))
                return i;
    }
    return -1;
}

到這里,大部分方法都基本上分析完了。

總結(jié)

  1. 優(yōu)先隊(duì)列是基于最小堆來(lái)實(shí)現(xiàn)的一種無(wú)界的隊(duì)列欣孤,所以馋没,隊(duì)頭出隊(duì)的元素總是最小的。該隊(duì)列不允許null元素降传,可以自定義優(yōu)先級(jí)篷朵,也就是比較規(guī)則;
  2. 該隊(duì)列不是線程安全的婆排,如果要在線程安全的場(chǎng)景下使用声旺,建議使用線程安全的PriorityBlockingQueue類;

本文參考自:
https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html
JDK源碼研究PriorityQueue(優(yōu)先隊(duì)列)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泽论,一起剝皮案震驚了整個(gè)濱河市艾少,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翼悴,老刑警劉巖缚够,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鹦赎,居然都是意外死亡谍椅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)古话,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雏吭,“玉大人,你說(shuō)我怎么就攤上這事陪踩≌让牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵肩狂,是天一觀的道長(zhǎng)摘完。 經(jīng)常有香客問(wèn)我,道長(zhǎng)傻谁,這世上最難降的妖魔是什么孝治? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮审磁,結(jié)果婚禮上谈飒,老公的妹妹穿的比我還像新娘。我一直安慰自己态蒂,他們只是感情好杭措,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著钾恢,像睡著了一般瓤介。 火紅的嫁衣襯著肌膚如雪吕喘。 梳的紋絲不亂的頭發(fā)上赘那,一...
    開(kāi)封第一講書(shū)人閱讀 49,785評(píng)論 1 290
  • 那天刑桑,我揣著相機(jī)與錄音,去河邊找鬼募舟。 笑死祠斧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拱礁。 我是一名探鬼主播琢锋,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呢灶!你這毒婦竟也來(lái)了吴超?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸯乃,失蹤者是張志新(化名)和其女友劉穎鲸阻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體缨睡,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸟悴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奖年。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片细诸。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陋守,靈堂內(nèi)的尸體忽然破棺而出震贵,到底是詐尸還是另有隱情,我是刑警寧澤水评,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布猩系,位于F島的核電站,受9級(jí)特大地震影響之碗,放射性物質(zhì)發(fā)生泄漏蝙眶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一褪那、第九天 我趴在偏房一處隱蔽的房頂上張望幽纷。 院中可真熱鬧,春花似錦博敬、人聲如沸友浸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)收恢。三九已至武学,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伦意,已是汗流浹背火窒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驮肉,地道東北人熏矿。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像慧域,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子论泛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

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