[原理探究]ArrayList遍歷時(shí)刪除元素的正確姿勢是什么?

本文已收錄到1.1K Star數(shù)開源學(xué)習(xí)指南——《大廠面試指北》,如果想要了解更多大廠面試相關(guān)的內(nèi)容及獲取《大廠面試指北》離線PDF版,請掃描下方二維碼碼關(guān)注公眾號“大廠面試”,謝謝大家了!

《大廠面試指北》最佳閱讀地址:

http://notfound9.github.io/interviewGuide/

《大廠面試指北》項(xiàng)目地址:

https://github.com/NotFound9/interviewGuide

獲取《大廠面試指北》離線PDF版,請掃描下方二維碼關(guān)注公眾號“大廠面試”

image.png

《大廠面試指北》項(xiàng)目截圖:

image.png

簡介

我們在項(xiàng)目開發(fā)過程中,經(jīng)常會有需求需要?jiǎng)h除ArrayList中的某個(gè)元素,而使用不正確的刪除方式,就有可能拋出異常。或者在面試中,會遇到面試官詢問遍歷時(shí)如何正常刪除元素。所以在本篇文章中,我們會對幾種刪除元素的方式進(jìn)行測試,并對原理進(jìn)行研究,希望可以幫助到大家!

ArrayList遍歷時(shí)刪除元素的幾種姿勢

首先結(jié)論如下:

第1種方法 - 普通for循環(huán)正序刪除(結(jié)果:會漏掉元素判斷)

第2種方法 - 普通for循環(huán)倒序刪除(結(jié)果:正確刪除)

第3種方法 - for-each循環(huán)刪除(結(jié)果:拋出異常)

第4種方法 - Iterator遍歷,使用ArrayList.remove()刪除元素(結(jié)果:拋出異常)

第5種方法 - Iterator遍歷,使用Iterator的remove刪除元素(結(jié)果:正確刪除)

下面讓我們來詳細(xì)探究一下原因吧!

首先初始化一個(gè)數(shù)組arrayList,假設(shè)我們要?jiǎng)h除等于3的元素。

   public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add(3);
        arrayList.add(4);
        arrayList.add(5);
        removeWayOne(arrayList);
    }

第1種方法 - 普通for循環(huán)正序刪除(結(jié)果:會漏掉元素判斷)

for (int i = 0; i < arrayList.size(); i++) {
    if (arrayList.get(i) == 3) {//3是要?jiǎng)h除的元素
        arrayList.remove(i);
        //解決方案: 加一行代碼i = i - 1; 刪除元素后,下標(biāo)減1
    }
    System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}
//原ArrayList是[1, 2, 3, 3, 4, 5]
//刪除后是[1, 2, 3, 4, 5]

輸出結(jié)果:

當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]

可以看到少刪除了一個(gè)3,

原因在于調(diào)用remove刪除元素時(shí),remove方法調(diào)用System.arraycopy()方法將后面的元素移動(dòng)到前面的位置,也就是第二個(gè)3會移動(dòng)到數(shù)組下標(biāo)為2的位置,而在下一次循環(huán)時(shí),i+1之后,i會為3,不會對數(shù)組下標(biāo)為2這個(gè)位置進(jìn)行判斷,所以這種寫法,在刪除元素時(shí),被刪除元素a的后一個(gè)元素b會移動(dòng)a的位置,而i已經(jīng)加1,會忽略對元素b的判斷,所以如果是連續(xù)的重復(fù)元素,會導(dǎo)致少刪除。

解決方案

可以在刪除元素后,執(zhí)行i=i-1,使得下次循環(huán)時(shí)再次對該數(shù)組下標(biāo)進(jìn)行判斷。

第2種方法 - 普通for循環(huán)倒序刪除(結(jié)果:正確刪除)

 for (int i = arrayList.size() -1 ; i>=0; i--) {
    if (arrayList.get(i).equals(3)) {
        arrayList.remove(i);
    }
    System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}

輸出結(jié)果:

當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]

這種方法可以正確刪除元素,因?yàn)檎{(diào)用remove刪除元素時(shí),remove方法調(diào)用System.arraycopy()將被刪除元素a后面的元素向前移動(dòng),而不會影響元素a之前的元素,所以倒序遍歷可以正常刪除元素。

第3種方法 - for-each循環(huán)刪除(結(jié)果:拋出異常)

public static void removeWayThree(ArrayList<Integer> arrayList) {
    for (Integer value : arrayList) {
        if (value.equals(3)) {//3是要?jiǎng)h除的元素
            arrayList.remove(value);
        }
    System.out.println("當(dāng)前arrayList是"+arrayList.toString());
    }
}

輸出結(jié)果:

當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.test.ArrayListTest1.removeWayThree(ArrayListTest1.java:50)
    at com.test.ArrayListTest1.main(ArrayListTest1.java:24)

會拋出ConcurrentModificationException異常,主要在于for-each的底層實(shí)現(xiàn)是使用ArrayList.iterator的hasNext()方法和next()方法實(shí)現(xiàn)的,我們可以使用反編譯進(jìn)行驗(yàn)證,對包含上面的方法的類使用以下命令反編譯驗(yàn)證

javac ArrayTest.java//生成ArrayTest.class文件
javap -c ArrayListTest.class//對class文件反編譯

得到removeWayThree方法的反編譯代碼如下:

 public static void removeWayThree(java.util.ArrayList<java.lang.Integer>);
    Code:
       0: aload_0
       1: invokevirtual #12   // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
       4: astore_1
       5: aload_1
       6: invokeinterface #13,  1 // InterfaceMethod java/util/Iterator.hasNext:()Z   調(diào)用Iterator.hasNext()方法
      11: ifeq          44
      14: aload_1
      15: invokeinterface #14,  1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;調(diào)用Iterator.next()方法
      20: checkcast     #9                  // class java/lang/Integer
      23: astore_2
      24: aload_2
      25: iconst_3
      26: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      29: invokevirtual #10                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z 
      32: ifeq          41
      35: aload_0
      36: aload_2
      37: invokevirtual #15                 // Method java/util/ArrayList.remove:(Ljava/lang/Object;)Z
      40: pop
      41: goto          5
      44: return

可以很清楚得看到Iterator.hasNext()來判斷是否還有下一個(gè)元素,和Iterator.next()方法來獲取下一個(gè)元素。而因?yàn)樵趧h除元素時(shí),remove()方法會調(diào)用fastRemove()方法,其中會對modCount+1,代表對數(shù)組進(jìn)行了修改,將修改次數(shù)+1。

 public boolean remove(Object o) {
     if (o == null) {
         for (int index = 0; index < size; index++)
             if (elementData[index] == null) {
                 fastRemove(index);
             return true;
         }
     } else {
         for (int index = 0; index < size; index++)
             if (o.equals(elementData[index])) {
                 fastRemove(index);
                 return true;
             }
     }
        return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,numMoved);
    elementData[--size] = null; // clear to let GC do its work
}


而當(dāng)刪除完元素后,進(jìn)行下一次循環(huán)時(shí),會調(diào)用下面源碼中Itr.next()方法獲取下一個(gè)元素,會調(diào)用checkForComodification()方法對ArrayList進(jìn)行校驗(yàn),判斷在遍歷ArrayList是否已經(jīng)被修改,由于之前對modCount+1,而expectedModCount還是初始化時(shí)ArrayList.Itr對象時(shí)賦的值,所以會不相等,然后拋出ConcurrentModificationException異常。

那么有什么辦法可以讓expectedModCount及時(shí)更新呢?

可以看到下面Itr的源碼中,在Itr.remove()方法中刪除元素后會對 expectedModCount更新,所以我們在使用刪除元素時(shí)使用Itr.remove()方法來刪除元素就可以保證expectedModCount的更新了,具體看第5種方法。

private class Itr implements Iterator<E> {
        int cursor;       // 游標(biāo)
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;//期待的modCount值

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();//判斷expectedModCount與當(dāng)前的modCount是否一致
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;//更新expectedModCount
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

第4種方法 - Iterator遍歷,使用ArrayList.remove()刪除元素(結(jié)果:拋出異常)

Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
    Integer value = iterator.next();
    if (value.equals(3)) {//3是要?jiǎng)h除的元素
            arrayList.remove(value);
    }
    System.out.println("當(dāng)前arrayList是"+arrayList.toString());
}

輸出結(jié)果:

當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.test.ArrayListTest1.removeWayFour(ArrayListTest1.java:61)
    at com.test.ArrayListTest1.main(ArrayListTest1.java:25)

第3種方法在編譯后的代碼,其實(shí)是跟第4種是一樣的,所以第四種寫法也會拋出ConcurrentModificationException異常。這種需要注意的是,每次調(diào)用iterator的next()方法,會導(dǎo)致游標(biāo)向右移動(dòng),從而達(dá)到遍歷的目的。所以在單次循環(huán)中不能多次調(diào)用next()方法,不然會導(dǎo)致每次循環(huán)時(shí)跳過一些元素,我在一些博客里面看到了一些錯(cuò)誤的寫法,比如這一篇《在ArrayList的循環(huán)中刪除元素,會不會出現(xiàn)問題?》文章中:

[圖片上傳失敗...(image-9f1ff1-1578232707248)]

先調(diào)用iterator.next()獲取元素,與elem進(jìn)行比較,如果相等,再調(diào)用list.remove(iterator.next());來移除元素,這個(gè)時(shí)候的iterator.next()其實(shí)已經(jīng)不是與elem相等的元素了,而是后一個(gè)元素了,我們可以寫個(gè)demo來測試一下

ArrayList<Integer> arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(6);
arrayList.add(7);

Integer elem = 3;
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
    System.out.println(arrayList);
    if(iterator.next().equals(elem)) {
            arrayList.remove(iterator.next());
    }
} 

輸出結(jié)果如下:

[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 5, 6, 7]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.test.ArrayListTest1.main(ArrayListTest1.java:29)

可以看到移除的元素其實(shí)不是3,而是3之后的元素,因?yàn)檎{(diào)用了兩次next()方法,導(dǎo)致游標(biāo)多移動(dòng)了。所以應(yīng)該使用Integer value = iterator.next();將元素取出進(jìn)行判斷。

第5種方法 - Iterator遍歷,使用Iterator的remove刪除元素(結(jié)果:正確刪除)

Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
    Integer value = iterator.next();
    if (value.equals(3)) {//3是需要?jiǎng)h除的元素
        iterator.remove();
    }
}

輸出結(jié)果:

當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 3, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]
當(dāng)前arrayList是[1, 2, 4, 5]

可以正確刪除元素。

跟第3種和第4種方法的區(qū)別在于是使用iterator.remove();來移除元素,而在remove()方法中會對iterator的expectedModCount變量進(jìn)行更新,所以在下次循環(huán)調(diào)用iterator.next()方法時(shí),expectedModCount與modCount相等,不會拋出異常。

HashMap遍歷時(shí)刪除元素的幾種姿勢

首先結(jié)論如下:

第1種方法 - for-each遍歷HashMap.entrySet,使用HashMap.remove()刪除(結(jié)果:拋出異常)。

第2種方法-for-each遍歷HashMap.keySet,使用HashMap.remove()刪除(結(jié)果:拋出異常)。

第3種方法-使用HashMap.entrySet().iterator()遍歷刪除(結(jié)果:正確刪除)。

下面讓我們來詳細(xì)探究一下原因吧!

HashMap的遍歷刪除方法與ArrayList的大同小異,只是api的調(diào)用方式不同。首先初始化一個(gè)HashMap,我們要?jiǎng)h除key包含"3"字符串的鍵值對。

HashMap<String,Integer> hashMap = new HashMap<String,Integer>();
hashMap.put("key1",1);
hashMap.put("key2",2);
hashMap.put("key3",3);
hashMap.put("key4",4);
hashMap.put("key5",5);
hashMap.put("key6",6);

第1種方法 - for-each遍歷HashMap.entrySet,使用HashMap.remove()刪除(結(jié)果:拋出異常)

for (Map.Entry<String,Integer> entry: hashMap.entrySet()) {
        String key = entry.getKey();
        if(key.contains("3")){
            hashMap.remove(entry.getKey());
        }
     System.out.println("當(dāng)前HashMap是"+hashMap+" 當(dāng)前entry是"+entry);

}

輸出結(jié)果:

當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key1=1
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key2=2
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key5=5
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前entry是key6=6
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 當(dāng)前entry是key3=3
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1463)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1461)
    at com.test.HashMapTest.removeWayOne(HashMapTest.java:29)
    at com.test.HashMapTest.main(HashMapTest.java:22)

第2種方法-for-each遍歷HashMap.keySet,使用HashMap.remove()刪除(結(jié)果:拋出異常)

Set<String> keySet = hashMap.keySet();
for(String key : keySet){
    if(key.contains("3")){
        keySet.remove(key);
    }
    System.out.println("當(dāng)前HashMap是"+hashMap+" 當(dāng)前key是"+key);
}

輸出結(jié)果如下:

當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key1
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key2
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key5
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key3=3, key4=4} 當(dāng)前key是key6
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 當(dāng)前key是key3
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1453)
    at com.test.HashMapTest.removeWayTwo(HashMapTest.java:40)
    at com.test.HashMapTest.main(HashMapTest.java:23)

第3種方法-使用HashMap.entrySet().iterator()遍歷刪除(結(jié)果:正確刪除)

Iterator<Map.Entry<String, Integer>> iterator  = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, Integer> entry = iterator.next();
    if(entry.getKey().contains("3")){
        iterator.remove();
    }
    System.out.println("當(dāng)前HashMap是"+hashMap+" 當(dāng)前entry是"+entry);
}

輸出結(jié)果:

當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key1=1
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key2=2
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key5=5
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key6=6
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4, deletekey=3} 當(dāng)前entry是key4=4
當(dāng)前HashMap是{key1=1, key2=2, key5=5, key6=6, key4=4} 當(dāng)前entry是deletekey=3

第1種方法和第2種方法拋出ConcurrentModificationException異常與上面ArrayList錯(cuò)誤遍歷-刪除方法的原因一致,HashIterator也有一個(gè)expectedModCount,在遍歷時(shí)獲取下一個(gè)元素時(shí),會調(diào)用next()方法,然后對
expectedModCount和modCount進(jìn)行判斷,不一致就拋出ConcurrentModificationException異常。

abstract class HashIterator {
    Node<K,V> next;        // next entry to return
    Node<K,V> current;     // current entry
    int expectedModCount;  // for fast-fail
    int index;             // current slot

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        if (t != null && size > 0) { // advance to first entry
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        if ((next = (current = e).next) == null && (t = table) != null) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }

    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}

PS:ConcurrentModificationException是什么?

根據(jù)ConcurrentModificationException的文檔介紹,一些對象不允許并發(fā)修改,當(dāng)這些修改行為被檢測到時(shí),就會拋出這個(gè)異常。(例如一些集合不允許一個(gè)線程一邊遍歷時(shí),另一個(gè)線程去修改這個(gè)集合)。

一些集合(例如Collection, Vector, ArrayList,LinkedList, HashSet, Hashtable, TreeMap, AbstractList, Serialized Form)的Iterator實(shí)現(xiàn)中,如果提供這種并發(fā)修改異常檢測,那么這些Iterator可以稱為是"fail-fast Iterator",意思是快速失敗迭代器,就是檢測到并發(fā)修改時(shí),直接拋出異常,而不是繼續(xù)執(zhí)行,等到獲取到一些錯(cuò)誤值時(shí)在拋出異常。

異常檢測主要是通過modCount和expectedModCount兩個(gè)變量來實(shí)現(xiàn)的,

  • modCount
    集合被修改的次數(shù),一般是被集合(ArrayList之類的)持有,每次調(diào)用add(),remove()方法會導(dǎo)致modCount+1

  • expectedModCount
    期待的modCount,一般是被Iterator(ArrayList.iterator()方法返回的iterator對象)持有,一般在Iterator初始化時(shí)會賦初始值,在調(diào)用Iterator的remove()方法時(shí)會對expectedModCount進(jìn)行更新。(可以看看上面的ArrayList.Itr源碼)

然后在Iterator調(diào)用next()遍歷元素時(shí),會調(diào)用checkForComodification()方法比較modCount和expectedModCount,不一致就拋出ConcurrentModificationException。

單線程操作Iterator不當(dāng)時(shí)也會拋出ConcurrentModificationException異常。(上面的例子就是)

總結(jié)

因?yàn)锳rrayList和HashMap的Iterator都是上面所說的“fail-fast Iterator”,Iterator在獲取下一個(gè)元素,刪除元素時(shí),都會比較expectedModCount和modCount,不一致就會拋出異常。

所以當(dāng)使用Iterator遍歷元素(for-each遍歷底層實(shí)現(xiàn)也是Iterator)時(shí),需要?jiǎng)h除元素,一定需要使用 Iterator的remove()方法 來刪除,而不是直接調(diào)用ArrayList或HashMap自身的remove()方法,否則會導(dǎo)致Iterator中的expectedModCount沒有及時(shí)更新,之后獲取下一個(gè)元素或者刪除元素時(shí),expectedModCount和modCount不一致,然后拋出ConcurrentModificationException異常。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評論 2 380

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