Stream流

在Java 8中弓千,得益于Lambda所帶來的函數(shù)式編程,引入了一個全新的Stream概念,用于解決已有集合類庫既有的 弊端捺僻。

1.1 引言

傳統(tǒng)集合的多步遍歷代碼

幾乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或間接的遍歷操作恋技。而當(dāng)我們需要對集合中的元 素進行操作的時候拇舀,除了必需的添加、刪除蜻底、獲取外骄崩,典型的就是集合遍歷。例如:

import java.util.ArrayList; 
import java.util.List;

public class Demo01ForEach {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三豐");
        for (String name : list) {            
            System.out.println(name);
        }
    } 
}
循環(huán)遍歷的弊端

Java 8的Lambda讓我們可以更加專注于做什么(What)薄辅,而不是怎么做(How)要拂,這點此前已經(jīng)結(jié)合內(nèi)部類進行 了對比說明。現(xiàn)在站楚,我們仔細體會一下上例代碼脱惰,可以發(fā)現(xiàn):

  • for循環(huán)的語法就是“怎么做
  • for循環(huán)的循環(huán)體才是“做什么

為什么使用循環(huán)?因為要進行遍歷窿春。但循環(huán)是遍歷的唯一方式嗎拉一?遍歷是指每一個元素逐一進行處理,而并不是從第一個到最后一個順次處理的循環(huán)旧乞。前者是目的蔚润,后者是方式。

試想一下尺栖,如果希望對集合中的元素進行篩選過濾:
1.將集合A根據(jù)條件一過濾為子集B抽碌;
2.然后再根據(jù)條件二過濾為子集C。

那怎么辦?在Java 8之前的做法可能為:

import java.util.ArrayList;
import java.util.List;

/*
    使用傳統(tǒng)的方式,遍歷集合,對集合中的數(shù)據(jù)進行過濾
 */
public class Demo01List {
    public static void main(String[] args) {
        //創(chuàng)建一個List集合,存儲姓名
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三豐");

        //對list集合中的元素進行過濾,只要以張開頭的元素,存儲到一個新的集合中
        List<String> listA = new ArrayList<>();
        for(String s : list){
            if(s.startsWith("張")){
                listA.add(s);
            }
        }

        //對listA集合進行過濾,只要姓名長度為3的人,存儲到一個新集合中
        List<String> listB = new ArrayList<>();
        for (String s : listA) {
            if(s.length()==3){
                listB.add(s);
            }
        }

        //遍歷listB集合
        for (String s : listB) {
            System.out.println(s);
        }
    }
}

這段代碼中含有三個循環(huán)货徙,每一個作用不同:

  1. 首先篩選所有姓張的人左权;
  2. 然后篩選名字有三個字的人;
  3. 后進行對結(jié)果進行打印輸出痴颊。

每當(dāng)我們需要對集合中的元素進行操作的時候赏迟,總是需要進行循環(huán)、循環(huán)蠢棱、再循環(huán)锌杀。這是理所當(dāng)然的么?不是泻仙。循環(huán)是做事情的方式糕再,而不是目的。另一方面玉转,使用線性循環(huán)就意味著只能遍歷一次突想。如果希望再次遍歷,只能再使 用另一個循環(huán)從頭開始究抓。

Stream的更優(yōu)寫法
import java.util.ArrayList;
import java.util.List;

/*
    使用Stream流的方式,遍歷集合,對集合中的數(shù)據(jù)進行過濾
    Stream流是JDK1.8之后出現(xiàn)的
    關(guān)注的是做什么,而不是怎么做
 */
public class Demo02Stream {
    public static void main(String[] args) {
        //創(chuàng)建一個List集合,存儲姓名
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三豐");

        //對list集合中的元素進行過濾,只要以張開頭的元素,存儲到一個新的集合中
        //對listA集合進行過濾,只要姓名長度為3的人,存儲到一個新集合中
        //遍歷listB集合
        list.stream()
            .filter(name->name.startsWith("張"))
            .filter(name->name.length()==3)
            .forEach(name-> System.out.println(name));
    }
}

1.2 流式思想概述

流式思想示意圖

這張圖中展示了過濾猾担、映射、跳過刺下、計數(shù)等多步操作绑嘹,這是一種集合元素的處理方案,而方案就是一種“函數(shù)模型”橘茉。圖中的每一個方框都是一個“流”工腋,調(diào)用指定的方法,可以從一個流模型轉(zhuǎn)換為另一個流模型畅卓。而最右側(cè)的數(shù)字3是最終結(jié)果擅腰。

這里的 filter 、 map 髓介、 skip 都是在對函數(shù)模型進行操作,集合元素并沒有真正被處理筋现。只有當(dāng)終結(jié)方法 count 執(zhí)行的時候唐础,整個模型才會按照指定策略執(zhí)行操作。而這得益于Lambda的延遲執(zhí)行特性矾飞。

備注:“Stream流”其實是一個集合元素的函數(shù)模型一膨,它并不是集合,也不是數(shù)據(jù)結(jié)構(gòu)洒沦,其本身并不存儲任何 元素(或其地址值)

Stream(流)是一個來自數(shù)據(jù)源的元素隊列

  • 元素是特定類型的對象豹绪,形成一個隊列。 Java中的Stream并不會存儲元素,而是按需計算瞒津。
  • 數(shù)據(jù)源 流的來源蝉衣。 可以是集合,數(shù)組 等巷蚪。

和以前的Collection操作不同病毡, Stream操作還有兩個基礎(chǔ)的特征:

  • Pipelining: 中間操作都會返回流對象本身。 這樣多個操作可以串聯(lián)成一個管道屁柏, 如同流式風(fēng)格(?uent style)啦膜。 這樣做可以對操作進行優(yōu)化, 比如延遲執(zhí)行(laziness)和短路( short-circuiting)淌喻。
  • 內(nèi)部迭代: 以前對集合遍歷都是通過Iterator或者增強for的方式, 顯式的在集合外部進行迭代僧家, 這叫做外部迭代。 Stream提供了內(nèi)部迭代的方式裸删,流可以直接調(diào)用遍歷方法八拱。

當(dāng)使用一個流的時候,通常包括三個基本步驟:獲取一個數(shù)據(jù)源(source)→ 數(shù)據(jù)轉(zhuǎn)換→執(zhí)行操作獲取想要的結(jié)果烁落,每次轉(zhuǎn)換原有 Stream 對象不改變乘粒,返回一個新的 Stream 對象(可以有多次轉(zhuǎn)換),這就允許對其操作可以像鏈條一樣排列伤塌,變成一個管道灯萍。

1.3 獲取流

java.util.stream.Stream<T> 是Java 8新加入的常用的流接口。(這并不是一個函數(shù)式接口每聪。)
獲取一個流非常簡單旦棉,有以下幾種常用的方式:

  • 所有的 Collection 集合都可以通過 stream 默認方法獲取流;
  • Stream 接口的靜態(tài)方法 of 可以獲取數(shù)組對應(yīng)的流药薯。
根據(jù)Collection獲取流

java.util.Collection 接口中加入了default方法 stream 用來獲取流绑洛,所以其所有實現(xiàn)類均可獲取流。

根據(jù)Map獲取流

java.util.Map 接口不是 Collection 的子接口童本,且其K-V數(shù)據(jù)結(jié)構(gòu)不符合流元素的單一特征炫狱,所以獲取對應(yīng)的流 需要分key、value或entry等情況逗爹。

根據(jù)數(shù)組獲取流

如果使用的不是集合或映射而是數(shù)組蛋哭,由于數(shù)組對象不可能添加默認方法,所以 Stream 接口中提供了靜態(tài)方法 of 泵额。

import java.util.*;
import java.util.stream.Stream;

/*
    java.util.stream.Stream<T>是Java 8新加入的最常用的流接口配深。(這并不是一個函數(shù)式接口。)
    獲取一個流非常簡單嫁盲,有以下幾種常用的方式:
        - 所有的Collection集合都可以通過stream默認方法獲取流篓叶;
            default Stream<E> stream?()
        - Stream接口的靜態(tài)方法of可以獲取數(shù)組對應(yīng)的流。
            static <T> Stream<T> of?(T... values)
            參數(shù)是一個可變參數(shù),那么我們就可以傳遞一個數(shù)組
 */
public class Demo01GetStream {
    public static void main(String[] args) {
        //把集合轉(zhuǎn)換為Stream流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Map<String,String> map = new HashMap<>();
        //獲取鍵,存儲到一個Set集合中
        Set<String> keySet = map.keySet();
        Stream<String> stream3 = keySet.stream();

        //獲取值,存儲到一個Collection集合中
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();

        //獲取鍵值對(鍵與值的映射關(guān)系 entrySet)
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();

        //把數(shù)組轉(zhuǎn)換為Stream流
        Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);
        //可變參數(shù)可以傳遞數(shù)組
        Integer[] arr = {1,2,3,4,5};
        Stream<Integer> stream7 = Stream.of(arr);
        String[] arr2 = {"a","bb","ccc"};
        Stream<String> stream8 = Stream.of(arr2);
    }
}

1.4 常用方法

流模型的操作很豐富,這里介紹一些常用的API缸托。這些方法可以被分成兩種:

  • 延遲方法:返回值類型仍然是 Stream 接口自身類型的方法左敌,因此支持鏈?zhǔn)秸{(diào)用。(除了終結(jié)方法外嗦董,其余方 法均為延遲方法母谎。)
  • 終結(jié)方法:返回值類型不再是 Stream 接口自身類型的方法,因此不再支持類似 StringBuilder 那樣的鏈?zhǔn)秸{(diào) 用京革。本小節(jié)中奇唤,終結(jié)方法包括 count 和 forEach 方法。

備注:本小節(jié)之外的更多方法匹摇,請自行參考API文檔咬扇。

逐一處理:forEach

forEach 方法用來遍歷流中的數(shù)據(jù)。

import java.util.stream.Stream;

/*
    Stream流中的常用方法_forEach
    void forEach(Consumer<? super T> action);
    該方法接收一個Consumer接口函數(shù)廊勃,會將每一個流元素交給該函數(shù)進行處理懈贺。
    Consumer接口是一個消費型的函數(shù)式接口,可以傳遞Lambda表達式,消費數(shù)據(jù)

    簡單記:
        forEach方法,用來遍歷流中的數(shù)據(jù)
        是一個終結(jié)方法,遍歷之后就不能繼續(xù)調(diào)用Stream流中的其他方法
 */
public class Stream_forEach {
    public static void main(String[] args) {
        //獲取一個Stream流
        Stream<String> stream = Stream.of("張三", "李四", "王五", "趙六", "田七");
        //使用Stream流中的方法forEach對Stream流中的數(shù)據(jù)進行遍歷
        /*stream.forEach((String name)->{
            System.out.println(name);
        });*/

        stream.forEach(name->System.out.println(name));
    }
}
過濾:filter

filter 方法將一個流轉(zhuǎn)換成另一個子集流。

import java.util.stream.Stream;

/*
    Stream流中的常用方法_filter:用于對Stream流中的數(shù)據(jù)進行過濾
    Stream<T> filter(Predicate<? super T> predicate);
    filter方法的參數(shù)Predicate是一個函數(shù)式接口,所以可以傳遞Lambda表達式,對數(shù)據(jù)進行過濾
    Predicate中的抽象方法:
        boolean test(T t);
 */
public class Stream_filter {
    public static void main(String[] args) {
        //創(chuàng)建一個Stream流
        Stream<String> stream = Stream.of("張三豐", "張翠山", "趙敏", "周芷若", "張無忌");
        //對Stream流中的元素進行過濾,只要姓張的人
        Stream<String> stream2 = stream.filter((String name)->{return name.startsWith("張");});
        //遍歷stream2流
        stream2.forEach(name-> System.out.println(name));

        /*
            Stream流屬于管道流,只能被消費(使用)一次
            第一個Stream流調(diào)用完畢方法,數(shù)據(jù)就會流轉(zhuǎn)到下一個Stream上
            而這時第一個Stream流已經(jīng)使用完畢,就會關(guān)閉了
            所以第一個Stream流就不能再調(diào)用方法了
            IllegalStateException: stream has already been operated upon or closed
         */
        //遍歷stream流
        stream.forEach(name-> System.out.println(name));
    }
}
映射:map

map 方法將流中的元素映射到另一個流中坡垫。

import java.util.stream.Stream;

/*
    Stream流中的常用方法_map:用于類型轉(zhuǎn)換
    如果需要將流中的元素映射到另一個流中梭灿,可以使用map方法.
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    該接口需要一個Function函數(shù)式接口參數(shù),可以將當(dāng)前流中的T類型數(shù)據(jù)轉(zhuǎn)換為另一種R類型的流冰悠。
    Function中的抽象方法:
        R apply(T t);
 */
public class Stream_map {
    public static void main(String[] args) {
        //獲取一個String類型的Stream流
        Stream<String> stream = Stream.of("1", "2", "3", "4");
        //使用map方法,把字符串類型的整數(shù),轉(zhuǎn)換(映射)為Integer類型的整數(shù)
        Stream<Integer> stream2 = stream.map((String s)->{
            return Integer.parseInt(s);
        });
        //遍歷Stream2流
        stream2.forEach(i-> System.out.println(i));
    }
}
統(tǒng)計個數(shù):count

count 方法統(tǒng)計流中元素個數(shù)堡妒,返回 long 類型。

import java.util.ArrayList;
import java.util.stream.Stream;

/*
    Stream流中的常用方法_count:用于統(tǒng)計Stream流中元素的個數(shù)
    long count();
    count方法是一個終結(jié)方法,返回值是一個long類型的整數(shù)
    所以不能再繼續(xù)調(diào)用Stream流中的其他方法了
 */
public class Stream_count {
    public static void main(String[] args) {
        //獲取一個Stream流
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        Stream<Integer> stream = list.stream();
        long count = stream.count();
        System.out.println(count);//7
    }
}
取用前幾個:limit

limit 方法可以對流進行截取溉卓,只取用前n個皮迟。

import java.util.stream.Stream;

/*
    Stream流中的常用方法_limit:用于截取流中的元素
    limit方法可以對流進行截取,只取用前n個桑寨。方法簽名:
    Stream<T> limit(long maxSize);
        參數(shù)是一個long型伏尼,如果集合當(dāng)前長度大于參數(shù)則進行截取尉尾;否則不進行操作
    limit方法是一個延遲方法,只是對流中的元素進行截取,返回的是一個新的流,所以可以繼續(xù)調(diào)用Stream流中的其他方法
 */
public class Stream_limit {
    public static void main(String[] args) {
        //獲取一個Stream流
        String[] arr = {"美羊羊","喜洋洋","懶洋洋","灰太狼","紅太狼"};
        Stream<String> stream = Stream.of(arr);
        //使用limit對Stream流中的元素進行截取,只要前3個元素
        Stream<String> stream2 = stream.limit(3);
        //遍歷stream2流
        stream2.forEach(name-> System.out.println(name));
    }
}
跳過前幾個:skip

skip 方法t跳過前幾個元素獲取一個截取之后的新流爆阶。

import java.util.stream.Stream;

/*
    Stream流中的常用方法_skip:用于跳過元素
    如果希望跳過前幾個元素,可以使用skip方法獲取一個截取之后的新流:
    Stream<T> skip(long n);
        如果流的當(dāng)前長度大于n沙咏,則跳過前n個辨图;否則將會得到一個長度為0的空流。
 */
public class Stream_skip {
    public static void main(String[] args) {
        //獲取一個Stream流
        String[] arr = {"美羊羊","喜洋洋","懶洋洋","灰太狼","紅太狼"};
        Stream<String> stream = Stream.of(arr);
        //使用skip方法跳過前3個元素
        Stream<String> stream2 = stream.skip(3);
        //遍歷stream2流
        stream2.forEach(name-> System.out.println(name));
    }
}
組合:concat

Stream 接口的靜態(tài)方法 concat 把兩個流合并成一個流

/*
    Stream流中的常用方法_concat:用于把流組合到一起
    如果有兩個流芭碍,希望合并成為一個流徒役,那么可以使用Stream接口的靜態(tài)方法concat
    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
 */
public class Stream_concat {
    public static void main(String[] args) {
        //創(chuàng)建一個Stream流
        Stream<String> stream1 = Stream.of("張三豐", "張翠山", "趙敏", "周芷若", "張無忌");
        //獲取一個Stream流
        String[] arr = {"美羊羊","喜洋洋","懶洋洋","灰太狼","紅太狼"};
        Stream<String> stream2 = Stream.of(arr);
        //把以上兩個流組合為一個流
        Stream<String> concat = Stream.concat(stream1, stream2);
        //遍歷concat流
        concat.forEach(name-> System.out.println(name));
    }
}
1.5 練習(xí):集合元素處理(傳統(tǒng)方式)

Person類

public class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

傳統(tǒng)方式

import java.util.ArrayList;

/*
    練習(xí):集合元素處理(傳統(tǒng)方式)
        現(xiàn)在有兩個ArrayList集合存儲隊伍當(dāng)中的多個成員姓名孽尽,要求使用傳統(tǒng)的for循環(huán)(或增強for循環(huán))依次進行以下若干操作步驟:
        1. 第一個隊伍只要名字為3個字的成員姓名窖壕;存儲到一個新集合中。
        2. 第一個隊伍篩選之后只要前3個人;存儲到一個新集合中瞻讽。
        3. 第二個隊伍只要姓張的成員姓名鸳吸;存儲到一個新集合中。
        4. 第二個隊伍篩選之后不要前2個人速勇;存儲到一個新集合中晌砾。
        5. 將兩個隊伍合并為一個隊伍;存儲到一個新集合中烦磁。
        6. 根據(jù)姓名創(chuàng)建Person對象养匈;存儲到一個新集合中。
        7. 打印整個隊伍的Person對象信息都伪。
 */
public class StreamTest {
    public static void main(String[] args) {
        //第一支隊伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪麗熱巴");
        one.add("宋遠橋");
        one.add("蘇星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("莊子");
        one.add("洪七公");
        //1. 第一個隊伍只要名字為3個字的成員姓名呕乎;存儲到一個新集合中。
        ArrayList<String> one1 = new ArrayList<>();
        for (String name : one) {
            if(name.length()==3){
                one1.add(name);
            }
        }
        //2. 第一個隊伍篩選之后只要前3個人陨晶;存儲到一個新集合中猬仁。
        ArrayList<String> one2 = new ArrayList<>();
        for (int i = 0; i <3 ; i++) {
            one2.add(one1.get(i));//i = 0,1,2
        }

        //第二支隊伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("張無忌");
        two.add("趙麗穎");
        two.add("張三豐");
        two.add("尼古拉斯趙四");
        two.add("張?zhí)鞇?);
        two.add("張二狗");
        //3. 第二個隊伍只要姓張的成員姓名;存儲到一個新集合中先誉。
        ArrayList<String> two1 = new ArrayList<>();
        for (String name : two) {
            if(name.startsWith("張")){
                two1.add(name);
            }
        }
        //4. 第二個隊伍篩選之后不要前2個人湿刽;存儲到一個新集合中。
        ArrayList<String> two2 = new ArrayList<>();
        for (int i = 2; i <two1.size() ; i++) {
            two2.add(two1.get(i)); //i 不包含0 1
        }

        //5. 將兩個隊伍合并為一個隊伍褐耳;存儲到一個新集合中诈闺。
        ArrayList<String> all = new ArrayList<>();
        all.addAll(one2);
        all.addAll(two2);

        //6. 根據(jù)姓名創(chuàng)建Person對象;存儲到一個新集合中漱病。
        ArrayList<Person> list = new ArrayList<>();
        for (String name : all) {
            list.add(new Person(name));
        }

        //7. 打印整個隊伍的Person對象信息买雾。
        for (Person person : list) {
            System.out.println(person);
        }
    }
}

1.6 練習(xí):集合元素處理(Stream方式)

import java.util.ArrayList;
import java.util.stream.Stream;

/*
    練習(xí):集合元素處理(Stream方式)
    將上一題當(dāng)中的傳統(tǒng)for循環(huán)寫法更換為Stream流式處理方式。
    兩個集合的初始內(nèi)容不變杨帽,Person類的定義也不變漓穿。
 */
public class StreamTest {
    public static void main(String[] args) {
        //第一支隊伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪麗熱巴");
        one.add("宋遠橋");
        one.add("蘇星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("莊子");
        one.add("洪七公");
        //1. 第一個隊伍只要名字為3個字的成員姓名;存儲到一個新集合中注盈。
        //2. 第一個隊伍篩選之后只要前3個人晃危;存儲到一個新集合中。
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);

        //第二支隊伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("張無忌");
        two.add("趙麗穎");
        two.add("張三豐");
        two.add("尼古拉斯趙四");
        two.add("張?zhí)鞇?);
        two.add("張二狗");
        //3. 第二個隊伍只要姓張的成員姓名老客;存儲到一個新集合中僚饭。
        //4. 第二個隊伍篩選之后不要前2個人;存儲到一個新集合中胧砰。
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("張")).skip(2);

        //5. 將兩個隊伍合并為一個隊伍鳍鸵;存儲到一個新集合中。
        //6. 根據(jù)姓名創(chuàng)建Person對象尉间;存儲到一個新集合中偿乖。
        //7. 打印整個隊伍的Person對象信息击罪。
        Stream.concat(oneStream,twoStream).map(name->new Person(name)).forEach(p-> System.out.println(p));
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贪薪,隨后出現(xiàn)的幾起案子媳禁,更是在濱河造成了極大的恐慌,老刑警劉巖画切,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竣稽,死亡現(xiàn)場離奇詭異,居然都是意外死亡霍弹,警方通過查閱死者的電腦和手機毫别,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來典格,“玉大人拧烦,你說我怎么就攤上這事《奂疲” “怎么了恋博?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長私恬。 經(jīng)常有香客問我债沮,道長,這世上最難降的妖魔是什么本鸣? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任疫衩,我火速辦了婚禮,結(jié)果婚禮上荣德,老公的妹妹穿的比我還像新娘闷煤。我一直安慰自己,他們只是感情好涮瞻,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布鲤拿。 她就那樣靜靜地躺著,像睡著了一般署咽。 火紅的嫁衣襯著肌膚如雪近顷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天宁否,我揣著相機與錄音窒升,去河邊找鬼。 笑死慕匠,一個胖子當(dāng)著我的面吹牛饱须,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播台谊,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼蓉媳,長吁一口氣:“原來是場噩夢啊……” “哼歹苦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起督怜,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狠角,沒想到半個月后号杠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡丰歌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年姨蟋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片立帖。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡眼溶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晓勇,到底是詐尸還是另有隱情堂飞,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布绑咱,位于F島的核電站绰筛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏描融。R本人自食惡果不足惜铝噩,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窿克。 院中可真熱鬧骏庸,春花似錦、人聲如沸年叮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽只损。三九已至硬猫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間改执,已是汗流浹背啸蜜。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辈挂,地道東北人衬横。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像终蒂,于是被迫代替她去往敵國和親蜂林。 傳聞我的和親對象是個殘疾皇子遥诉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355