Java1.8-Arrays源碼解析

概述

集合框架的工具類除了Collecions之外锦针,另一個(gè)就是Arrays了赫舒,上文已經(jīng)說(shuō)過(guò)脚曾,Java中Arrays工具類是針對(duì)數(shù)組進(jìn)行操作的,這個(gè)類中包含了許多操作數(shù)組的靜態(tài)方法割以。我們來(lái)大致看一下它的源碼實(shí)現(xiàn)金度。

構(gòu)造方法與屬性
private Arrays() {}
// 進(jìn)行并行排序的最小數(shù)組長(zhǎng)度
private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
// 在使用歸并排序中優(yōu)先使用插入排序的閾值(后續(xù)會(huì)移除掉)
private static final int INSERTIONSORT_THRESHOLD = 7;

和Collections一樣,構(gòu)造方法私有严沥,不對(duì)外提供猜极。我們只需調(diào)用Arrays的靜態(tài)方法來(lái)操作數(shù)據(jù)即可。

sort方法

Arrays提供了一系列重載的sort方法消玄,大體上可以分為兩種跟伏,一種是針對(duì)基本數(shù)據(jù)類型來(lái)進(jìn)行排序丢胚,包括int,long受扳,byte携龟,float,double等類型勘高,另一種是針對(duì)Object數(shù)組類型來(lái)進(jìn)行排序峡蟋。默認(rèn)都是升序排列的。我們來(lái)簡(jiǎn)單看一下int數(shù)組的排序:

public static void sort(int[] a) {
    DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
// 對(duì)數(shù)組的一部分進(jìn)行排序
public static void sort(int[] a, int fromIndex, int toIndex) {
    // 檢測(cè)數(shù)組的下標(biāo)范圍
    rangeCheck(a.length, fromIndex, toIndex);
    DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
}

??可以看到华望,底層都是通過(guò)調(diào)用DualPivotQuicksort該類的sort方法來(lái)實(shí)現(xiàn)的蕊蝗。DualPivotQuicksort這個(gè)類是JAVA1.7之后專門(mén)提供給Java內(nèi)部排序使用的專用類,被稱為雙樞軸快速排序赖舟,用來(lái)優(yōu)化原先的快速排序蓬戚,該算法一般能提供O(nlog(n))的時(shí)間復(fù)雜度,大家有興趣的可以查看它的源碼來(lái)學(xué)習(xí)一下宾抓。
??至于其他類型的排序碌更,和int類型排序都是類似的,不多說(shuō)了洞慎。另外簡(jiǎn)單說(shuō)一點(diǎn),就是我們使用的Collections的sort方法就是借助Arrays.sort方法來(lái)實(shí)現(xiàn)的嘿棘。

另外一種重載的sort方法:

public static void sort(Object[] a)
public static void sort(Object[] a, int fromIndex, int toIndex)
// 帶比較器的排序算法
public static <T> void sort(T[] a, Comparator<? super T> c)
// 帶比較器的 數(shù)組的部分排序
public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c)

該方法要求傳入的參數(shù)必須實(shí)現(xiàn)了Comparable劲腿,這個(gè)排序算法是一種穩(wěn)定的算法。由于實(shí)現(xiàn)大致相似鸟妙,我們來(lái)簡(jiǎn)單看下sort(Object[] a)焦人。

public static void sort(Object[] a) {
    // 使用歸并排序
    if (LegacyMergeSort.userRequested)
        legacyMergeSort(a);
    // 使用TimSort排序
    else
        ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}

該算法的實(shí)現(xiàn)又分為兩種,一種通過(guò)歸并排序算法來(lái)實(shí)現(xiàn)重父,另一種通過(guò)使用TimSort排序算法來(lái)實(shí)現(xiàn)花椭。TimSort算法是一種插入與傳統(tǒng)歸并算法結(jié)合的一種算法,是對(duì)歸并算法的一種優(yōu)化房午。至于使用哪一種算法矿辽,需要設(shè)置系統(tǒng)變量:java.util.Arrays.useLegacyMergeSort,通過(guò)設(shè)置為true郭厌,來(lái)使用歸并算法袋倔,否則使用TimSort算法。默認(rèn)情況下我們是不會(huì)用到歸并算法的折柠,并且在JDK文檔中有說(shuō)明宾娜,在后續(xù)的版本中,legacyMergeSort歸并算法會(huì)被移除掉扇售。所以前塔,如果有興趣嚣艇,大家可以去看下TimSort算法的實(shí)現(xiàn)。這里參考自:
Compile in Java 6, run in 7 - how to specify useLegacyMergeSort?

/**
 * Old merge sort implementation can be selected (for
 * compatibility with broken comparators) using a system property.
 * Cannot be a static boolean in the enclosing class due to
 * circular dependencies. To be removed in a future release.
 */
static final class LegacyMergeSort {
    private static final boolean userRequested =
        java.security.AccessController.doPrivileged(
            new sun.security.action.GetBooleanAction(
                "java.util.Arrays.useLegacyMergeSort")).booleanValue();
}

/** To be removed in a future release. */
private static void legacyMergeSort(Object[] a) {
    Object[] aux = a.clone();
    mergeSort(aux, a, 0, a.length, 0);
}
parallelSort方法

??Arrays同樣提供了一系列重載的parallelSort方法华弓,用于數(shù)字類型的并行排序食零,同樣默認(rèn)也是升序排列的。這一系列算法是JAVA1.8之后引入的该抒,基于JAVA的并行處理框架fork/join框架慌洪,而fork/join框架,是Java1.7引入凑保,目的是為了充分利用多核處理器冈爹,編寫(xiě)并行程序,提高程序性能的框架欧引。

public static void parallelSort(byte[] a) {
    int n = a.length, p, g;
    // 如果數(shù)組長(zhǎng)度小于并行排序的最小長(zhǎng)度或者并行線程池的大小是1
    if (n <= MIN_ARRAY_SORT_GRAN ||
        (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
        DualPivotQuicksort.sort(a, 0, n - 1);
    else
        new ArraysParallelSortHelpers.FJByte.Sorter
            (null, a, new byte[n], 0, n, 0,
             ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
             MIN_ARRAY_SORT_GRAN : g).invoke();
}

我們可以看到频伤,程序里進(jìn)行了判斷,如果數(shù)組長(zhǎng)度太小芝此,小于并行排序的最小長(zhǎng)度或者并行線程池的大小是1憋肖,這時(shí)候就不再使用并行處理,還是使用DualPivotQuicksort的sort方法來(lái)進(jìn)行排序婚苹。這里有兩點(diǎn)簡(jiǎn)單說(shuō)明下:

  1. Arrays的常量MIN_ARRAY_SORT_GRAN 岸更,如果數(shù)組容量太小,而使用并行處理的話膊升,通常會(huì)導(dǎo)致跨任務(wù)的內(nèi)存競(jìng)爭(zhēng)怎炊,這樣的話并行的速度就沒(méi)有預(yù)想中的那么令人滿意。
  2. ForkJoinPool是fork/join框架中的一個(gè)核心類廓译,通過(guò)ForkJoinPool的getCommonPoolParallelism方法我們可以獲取到并行線程池的大小评肆,如果是1的話,也就是單核非区,就不需要使用并行處理了瓜挽。因?yàn)槟J(rèn)情況下,并行線程池的大小等于CPU的核數(shù)征绸。
parallelPrefix方法

public static void parallelPrefix(int[] array, IntBinaryOperator op)
public static <T> void parallelPrefix(T[] array, int fromIndex,int toIndex, BinaryOperator<T> op)

Arrays提供了一系列parallelPrefix方法久橙,用于對(duì)數(shù)組進(jìn)行并行的二元迭代操作,其中方法的最后一個(gè)參數(shù)是二元操作符的意思管怠,該運(yùn)算符其實(shí)是一個(gè)函數(shù)表達(dá)式剥汤。說(shuō)起來(lái)有點(diǎn)繞口,先看個(gè)例子:

public static void main(String[] args) {
    int[] numbers = {1, 2, 3, 4, 5};
    System.out.println("old array: ");
    Arrays.stream(numbers).forEach(n -> System.out.print(n + " "));

    // 二元迭代:累加
    System.out.println("\noperator: + ");
    IntBinaryOperator binaryOperator = (x, y) -> (x + y);
    Arrays.parallelPrefix(numbers, binaryOperator);
    Arrays.stream(numbers).forEach(n -> System.out.print(n + " "));

    // 二元迭代:累乘
    System.out.println("\noperator: * ");
    Arrays.parallelPrefix(numbers, (x, y) -> x * y);
    Arrays.stream(numbers).forEach(n -> System.out.print(n + " "));
}

output:

old array: 
1 2 3 4 5 
operator: + 
1 3 6 10 15 
operator: * 
1 3 18 180 2700 

例子參考自:Java 8 Arrays parallelPrefix method Example
也就是說(shuō)排惨,該方法是一種累計(jì)的操作吭敢,比如累加,數(shù)組中的每個(gè)元素依次與前面的所有元素累加暮芭,然后替換原來(lái)的元素鹿驼。對(duì)于大型數(shù)組欲低,并行迭代操作通常比順序循環(huán)性能更好。同樣畜晰,該方法也有多種重載方式砾莱,支持int,long凄鼻,double等類型的操作腊瑟。

該方法是JDK1.8之后引入的,底層是通過(guò)JDK內(nèi)部提供的ArrayPrefixHelpers類來(lái)實(shí)現(xiàn)的块蚌,當(dāng)然最終還是通過(guò)fork/join框架來(lái)實(shí)現(xiàn)的闰非。

源碼如下:

public static void parallelPrefix(int[] array, IntBinaryOperator op) {
    Objects.requireNonNull(op);
    if (array.length > 0)
        new ArrayPrefixHelpers.IntCumulateTask
                (null, op, array, 0, array.length).invoke();
}
binarySearch方法

public static int binarySearch(int[] a, int key)
public static int binarySearch(int[] a, int fromIndex, int toIndex,int key)

顧名思義,這個(gè)方法是二分查找的意思峭范,用來(lái)在數(shù)組中查找元素财松。當(dāng)然,使用該方法之前纱控,必須保證該數(shù)組已經(jīng)排序完成辆毡,并且是升序完成。否則甜害,查找將沒(méi)有什么意義舶掖。如果數(shù)組包含多個(gè)指定查找值的元素,則無(wú)法保證找到具體哪一個(gè)值尔店。
源碼如下:

public static int binarySearch(int[] a, int key) {
    return binarySearch0(a, 0, a.length, key);
}

/**
 * 其中該方法和binarySearch的public方法一樣眨攘,就是沒(méi)有索引校驗(yàn)
 */
 // Like public version, but without range checks.
private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                 int key) {
    // 開(kāi)始下標(biāo)
    int low = fromIndex;
    // 結(jié)束下標(biāo)
    int high = toIndex - 1;

    while (low <= high) {
        // 通過(guò)位移運(yùn)算符計(jì)算中間下標(biāo)值
        int mid = (low + high) >>> 1;
        // 保存數(shù)組中間值
        int midVal = a[mid];
        // 中間值比要查找的值小,往數(shù)組高位進(jìn)行查詢
        if (midVal < key)
            low = mid + 1;
        // 中間值比要查找的值大闹获,往數(shù)組低位進(jìn)行查詢
        else if (midVal > key)
            high = mid - 1;
        else
            // 如果相等,直接返回
            return mid; // key found
    }
    // 查找不到河哑,返回負(fù)值
    return -(low + 1);  // key not found.
}

該方法比較簡(jiǎn)單避诽,當(dāng)然也是提供了一系列重載的方法,其中也可以自定義查找時(shí)的比較規(guī)則:
private static <T> int binarySearch0(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c)
大家等用到的時(shí)候可自行查看璃谨。

equals方法

public static boolean equals(Object[] a, Object[] a2)

這個(gè)方法很簡(jiǎn)單沙庐,就是判斷兩個(gè)數(shù)組是否相等。比較的類型如果重寫(xiě)了equals方法佳吞,則按照對(duì)象的equals方法進(jìn)行比較拱雏。該方法也有許多重載方法,實(shí)現(xiàn)類似底扳,我們來(lái)查看下object數(shù)組作為參數(shù)的源碼:

public static boolean equals(Object[] a, Object[] a2) {
    // 如果是同一個(gè)數(shù)組引用铸抑,則相等
    if (a==a2)
        return true;
    // 如果有一個(gè)數(shù)組為null,則不相等
    if (a==null || a2==null)
        return false;

    int length = a.length;
    // 如果長(zhǎng)度不相等衷模,則不相等
    if (a2.length != length)
        return false;
    // 如果對(duì)應(yīng)位置上的值不相等鹊汛,則不相等
    for (int i=0; i<length; i++) {
        Object o1 = a[i];
        Object o2 = a2[i];
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }
    如果以上都不滿足蒲赂,則相等
    return true;
}
fill方法

public static void fill(long[] a, long val)
public static void fill(long[] a, int fromIndex, int toIndex, long val)
這個(gè)方法也很簡(jiǎn)單,就是將指定值填充到數(shù)組的每個(gè)元素刁憋。底層實(shí)現(xiàn)就是循環(huán)遍歷填充即可滥嘴。一般我們批量初始化數(shù)組的時(shí)候可以使用該方法。

public static void fill(int[] a, int val) {
    for (int i = 0, len = a.length; i < len; i++)
        a[i] = val;
}
copyOf和copyOfRange方法

public static int[] copyOf(int[] original, int newLength)
public static <T> T[] copyOfRange(T[] original, int from, int to)

復(fù)制數(shù)組至耻,newLength是新數(shù)組的長(zhǎng)度若皱。如果新數(shù)組長(zhǎng)度大于原數(shù)組長(zhǎng)度,則新值填充null或相關(guān)類型默認(rèn)值(比如int尘颓,默認(rèn)填充0走触,double類型默認(rèn)為0.0)。先來(lái)看下泛型的源碼:

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

可以看到泥耀,底層是通過(guò)System的native方法arraycopy來(lái)實(shí)現(xiàn)的饺汹,這個(gè)方法在操作集合的時(shí)候也經(jīng)常用到。而對(duì)于類型固定的則和這類似痰催,看一下int類型:

public static int[] copyOf(int[] original, int newLength) {
    int[] copy = new int[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

而copyOfRange底層實(shí)現(xiàn)類似兜辞,就多了步對(duì)索引的判斷,就不多說(shuō)了夸溶。

asList

將數(shù)組轉(zhuǎn)為L(zhǎng)ist逸吵,該方法與集合的toArray方法一起充當(dāng)了基于數(shù)組和集合之間的橋梁。并且該方法還提供了一種很便捷的方法來(lái)創(chuàng)建一個(gè)初始化大小的列表缝裁,該列表初始化包含幾個(gè)元素:

List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");

該方法源碼如下:

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

這里返回的ArrayList不是我們常用的java.util.ArrayList扫皱,而是Arrays的一個(gè)靜態(tài)內(nèi)部類,并且該內(nèi)部類中沒(méi)有add和remove方法捷绑,所以不支持添加和移除等操作韩脑。

private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable
{
    private static final long serialVersionUID = -2764017481108945198L;
    private final E[] a;

    ArrayList(E[] array) {
        a = Objects.requireNonNull(array);
    }

    @Override
    public int size() {
        return a.length;
    }

    @Override
    public Object[] toArray() {
        return a.clone();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        ...
    }

    @Override
    public E get(int index) {
        return a[index];
    }

    @Override
    public E set(int index, E element) {
        E oldValue = a[index];
        a[index] = element;
        return oldValue;
    }

    @Override
    public int indexOf(Object o) {
        ...
    }

    @Override
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    @Override
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(a, Spliterator.ORDERED);
    }

    @Override
    public void forEach(Consumer<? super E> action) {
        ...
    }

    @Override
    public void replaceAll(UnaryOperator<E> operator) {
        ...
    }

    @Override
    public void sort(Comparator<? super E> c) {
        Arrays.sort(a, c);
    }
}
hashCode方法

??獲取數(shù)組的hashCode值,該值是基于數(shù)組的每一個(gè)元素的hashCode來(lái)實(shí)現(xiàn)的粹污。
??如果數(shù)組中有嵌套數(shù)組段多,我們還想根據(jù)里層數(shù)組的每個(gè)元素的hashCode來(lái)獲取hash的話,我們可以通過(guò)deepHashCode方法壮吩,該方法通過(guò)遞歸的方式來(lái)實(shí)現(xiàn)进苍。

通俗的來(lái)講,hashCode方法只計(jì)算到數(shù)組的第一層鸭叙,而deepHashCode方法則會(huì)一直遞歸調(diào)用到數(shù)組無(wú)法再拆分為止觉啊。

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;
    int result = 1;
    // 遍歷每個(gè)元素,調(diào)用每個(gè)元素的hashCode方法
    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());
    return result;
}

大概看一下deepHashCode的方法:

for (Object element : a) {
            int elementHash = 0;
            if (element instanceof Object[])
                elementHash = deepHashCode((Object[]) element);
            else if (element instanceof byte[])
                elementHash = hashCode((byte[]) element);
            else if (element instanceof short[])
                elementHash = hashCode((short[]) element);
            else if (element instanceof int[])
                elementHash = hashCode((int[]) element);
            else if (element instanceof long[])
                elementHash = hashCode((long[]) element);
            else if (element instanceof char[])
                elementHash = hashCode((char[]) element);
            else if (element instanceof float[])
                elementHash = hashCode((float[]) element);
            else if (element instanceof double[])
                elementHash = hashCode((double[]) element);
            else if (element instanceof boolean[])
                elementHash = hashCode((boolean[]) element);
            else if (element != null)
                elementHash = element.hashCode();

            result = 31 * result + elementHash;
        }
deepEquals方法

同樣沈贝,對(duì)于數(shù)組的equals方法杠人,也是只比較第一層,如果有多層嵌套的話,我們可以使用deepEquals方法來(lái)比較搜吧。

toString方法

這個(gè)方法我們經(jīng)常用到市俊,這里就是返回?cái)?shù)組的字符串表示形式,我們可以更直觀的看到數(shù)組里保存的都是什么滤奈。

public static String toString(Object[] a) {
    if (a == null)
        return "null";

    int iMax = a.length - 1;
    if (iMax == -1)
        return "[]";
    // 內(nèi)部使用StringBuilder來(lái)表示
    StringBuilder b = new StringBuilder();
    b.append('[');
    for (int i = 0; ; i++) {
        b.append(String.valueOf(a[i]));
        if (i == iMax)
            return b.append(']').toString();
        b.append(", ");
    }
}

同理摆昧,還有一個(gè)deepToString方法,用來(lái)返回嵌套數(shù)組的字符串表示形式蜒程。這里就不多說(shuō)了绅你。

setAll方法

public static <T> void setAll(T[] array, IntFunction<? extends T> generator)

使用生成器函數(shù)更新數(shù)組的每個(gè)元素,其中最后一個(gè)參數(shù)是JDK1.8之后的一個(gè)一元操作符昭躺,也是函數(shù)式接口忌锯。我們先看一個(gè)例子:

public static void main(String[] args) {
    int[] number = {1, 2, 3, 4, 5, 6};
    Arrays.setAll(number, x -> x * 3);
    System.out.println(Arrays.toString(number));
}

output:

[0, 3, 6, 9, 12, 15]

上述方法是為數(shù)組的每個(gè)元素乘以3,重新賦值給數(shù)組领炫。
源碼如下:

public static <T> void setAll(T[] array, IntFunction<? extends T> generator) {
    Objects.requireNonNull(generator);
    for (int i = 0; i < array.length; i++)
        array[i] = generator.apply(i);
}

通過(guò)調(diào)用函數(shù)式接口generator的apply方法來(lái)實(shí)現(xiàn)一元運(yùn)算符操作偶垮。

parallelSetAll方法

同樣,parallelSetAll方法是setAll的并行方式帝洪,通過(guò)借助JDK1.8的IntStream對(duì)象來(lái)實(shí)現(xiàn)似舵。

public static <T> void parallelSetAll(T[] array, IntFunction<? extends T> generator) {
    Objects.requireNonNull(generator);
    IntStream.range(0, array.length).parallel().forEach(i -> { array[i] = generator.apply(i); });
}
spliterator方法

JDK8之后引入的并行迭代器,目的就是并行遍歷數(shù)組中的元素葱峡⊙饣可以與原先的Iterator迭代器相比較。Spliterator是很有意思的一個(gè)類砰奕,如果有時(shí)間蛛芥,再單獨(dú)分析一下,現(xiàn)在先簡(jiǎn)單看一下源碼即可:

public static <T> Spliterator<T> spliterator(T[] array) {
    return Spliterators.spliterator(array,Spliterator.ORDERED | Spliterator.IMMUTABLE);
}
stream方法

Stream也是JDK8之后引入的用于處理集合或者數(shù)組等類型的流军援,一般Stream配合lambda表達(dá)式使用仅淑,可以極大的提高我們編程的效率和程序的可讀性。對(duì)于Stream胸哥,我們也是等有時(shí)間了單獨(dú)分析一下涯竟,現(xiàn)在也是先簡(jiǎn)單看一下源碼,順便看一下簡(jiǎn)單的實(shí)現(xiàn):

public static <T> Stream<T> stream(T[] array) {
    return stream(array, 0, array.length);
}

public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) {
    return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false);
}

看一下簡(jiǎn)單的實(shí)現(xiàn):

public static void main(String[] args) {
    int[] number = {1, 2, 3, 4, 5, 6};
    Arrays.stream(number).forEach(n -> System.out.print(n + " "));
}

output:

1 2 3 4 5 6 

總結(jié)

Arrays的源碼到現(xiàn)在基本上已經(jīng)看完了烘嘱,我們可以做個(gè)大致的總結(jié):

  1. Arrays的源碼大致分為三種:parallel開(kāi)頭的都是并行處理的昆禽,deep開(kāi)頭的都是用于數(shù)組嵌套相關(guān)的操作蝗蛙,另一種就是我們常用的簡(jiǎn)單操作蝇庭;
  2. Arrays中基本上每個(gè)方法都有一系列的重載方法用于滿足不同類型的操作,我們可以根據(jù)需要選擇性的調(diào)用捡硅。

本文參考的鏈接如下:
https://docs.oracle.com/javase/8/docs/api/
讀 Java Arrays 源碼 筆記
快速排序?qū)崿F(xiàn)及優(yōu)化 | DualPivotQuicksort
Java8里面的java.util.Spliterator接口有什么用哮内?

參考網(wǎng)站主要是:stackoverflow,搜索引擎:Google。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末北发,一起剝皮案震驚了整個(gè)濱河市纹因,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琳拨,老刑警劉巖瞭恰,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狱庇,居然都是意外死亡惊畏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)密任,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)颜启,“玉大人,你說(shuō)我怎么就攤上這事浪讳$终担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵淹遵,是天一觀的道長(zhǎng)口猜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)合呐,這世上最難降的妖魔是什么暮的? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮淌实,結(jié)果婚禮上冻辩,老公的妹妹穿的比我還像新娘。我一直安慰自己拆祈,他們只是感情好恨闪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著放坏,像睡著了一般咙咽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淤年,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天钧敞,我揣著相機(jī)與錄音,去河邊找鬼麸粮。 笑死溉苛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弄诲。 我是一名探鬼主播愚战,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了寂玲?” 一聲冷哼從身側(cè)響起塔插,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拓哟,沒(méi)想到半個(gè)月后想许,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡断序,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年伸刃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逢倍。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捧颅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出较雕,到底是詐尸還是另有隱情碉哑,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布亮蒋,位于F島的核電站扣典,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏慎玖。R本人自食惡果不足惜贮尖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望趁怔。 院中可真熱鬧湿硝,春花似錦、人聲如沸润努。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)铺浇。三九已至痢畜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳍侣,已是汗流浹背丁稀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留倚聚,地道東北人线衫。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像秉沼,于是被迫代替她去往敵國(guó)和親桶雀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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