Java8特性——Stream

為什么需要Stream
Java 8 中的Stream是對(duì)集合 (Collection) 對(duì)象功能的增強(qiáng), 他專(zhuān)注于對(duì)集合對(duì)象進(jìn)行各種非常便利,高效的聚合操作(aggregate operation), 或者大批量數(shù)據(jù)操作 (bulk data operation).
Stream API借助于同樣新出現(xiàn)的Lambda 表達(dá)式, 極大的提高編程效率和程序可讀性. 同時(shí)他提供穿行和并行兩種模式進(jìn)行匯聚操作, 并發(fā)模式能夠成分利用多核處理器的優(yōu)勢(shì), 使用fork/join 并行法師來(lái)拆分任務(wù)和加速處理過(guò)程.
通常編寫(xiě)并行代碼很難而且容易出錯(cuò), 但使用Steam API無(wú)需編寫(xiě)一行多線程的代碼, 就可以很方便地寫(xiě)出高性能的并發(fā)代碼睬涧。

幾個(gè)例子:

1. Java 8的排序募胃,取值實(shí)現(xiàn)

List<Integer> transactionsIds =
transactions.stream().filter(t -> t.getType() == Transaction.Type.GEOCERY)
    .sorted(Comparator.comparing(Transaction::getValue).reversed())//排序
    .map(Transaction::getId)//取出id組裝新stream
    .collect(Collectors.toList());
System.out.println(transactionsIds);//[6, 5, 3, 1]

2. 流轉(zhuǎn)換為其他數(shù)據(jù)結(jié)構(gòu)(collect)

一個(gè)Stream只可以使用一次,否則會(huì)報(bào)錯(cuò)

        Stream stream = Stream.of("a1", "b1", "c1");
        // 1. Array
        String[] strArray1 = (String[]) stream.toArray(String[]::new);
        for (String s : strArray1) { System.out.print(s); } //a1b1c1
        // 2.Collection list
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        List<String> list1 = (List<String>) stream.collect(Collectors.toList());
        for (String s : list1) { System.out.print(s); }//a1b1c1
        // 2.Collection list
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        List<String> list2 = (List<String>) stream.collect(Collectors.toCollection(ArrayList::new));
        for (String s : list2) { System.out.print(s); } //a1b1c1
        // 2.Collection set
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        Set<String> set = (Set<String>) stream.collect(Collectors.toSet());
        for (String s : set) { System.out.print(s); } //a1c1b1
        // 2.Collection stack
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        Stack<String> stack = (Stack<String>) stream.collect(Collectors.toCollection(Stack::new));
        for (String s : stack) { System.out.print(s); } //a1b1c1
        // 3. String
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        String str = stream.collect(Collectors.joining()).toString();
        System.out.print(str); // a1b1c1
        String str = stream.collect(Collectors.joining("&")).toString();
        System.out.print(str); // a1&b1&c1
        String str = stream.collect(Collectors.joining("&","prefix","suffix")).toString();
        System.out.print(str); // prefixa1&b1&c1suffix
        //4. stream元素?cái)?shù)量
        Object str = stream.collect(Collectors.counting());
        System.out.print(str); // 3
        //5. groupby
        Object str = stream.collect(Collectors.groupingBy(o -> o));
        System.out.print(str); // {a1=[a1], c1=[c1], b1=[b1]}
        Stream stream = Stream.of("a1", "a1", "b1", "c1");// 分組求和
        Object str = stream.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        System.out.print(str); // {a1=2, c1=1, b1=1}
        //自定義對(duì)象分組求和
        Map<String, Integer> sumMap = fruitList.stream().collect.
        (Collectors.groupingBy(Fruit::getName, Collectors.summingInt(Fruit::getPrice)));

3. 打印姓名 (forEach 和pre-java8的對(duì)比)

forEach 不能修改自己包含的本地變量值畦浓,也不能用break/return 之類(lèi)的關(guān)鍵字提前結(jié)束循環(huán)痹束,只能進(jìn)行全部值迭代后處理

       // JDK 8
        roster.stream().filter(p -> p.gender == Person.Sex.MALE)
                       .forEach(p -> System.out.println(p.name));
        // JDK 7
        for (Person p : roster) {
            if(p.gender == Person.Sex.MALE){
                System.out.println(p.name);
            }
        }

4. peek 對(duì)每個(gè)元素執(zhí)行操作并且返回一個(gè)新的Stream

【peek : 偷窺】注意執(zhí)行順序

  Stream.of("one", "two", "three", "four")
                .filter(p -> p.length() > 3)
                .peek(v -> System.out.println("Filtered Value:" + v))
                .map(String::toUpperCase)
                .peek(v -> System.out.println("Mapped Value:" + v))
                .collect(Collectors.toList());
        // 1. Filtered Value:three
        // 2. Mapped Value:THREE
        // 3. Filtered Value:four
        // 4. Mapped Value:FOUR

5.reduce的用例

使用方式說(shuō)明
  • 方式一
    一個(gè)參數(shù),reduce(BinaryOperator<T> accumulator)方法需要一個(gè)函數(shù)式接口參數(shù)讶请,該函數(shù)式接口需要兩個(gè)參數(shù)祷嘶,返回一個(gè)結(jié)果(reduce中返回的結(jié)果會(huì)作為下次累加器計(jì)算的第一個(gè)參數(shù)),也就是累加器秽梅。
  • 方式二
    2個(gè)參數(shù),T reduce(T identity, BinaryOperator<T> accumulator)
    提供一個(gè)跟Stream中數(shù)據(jù)同類(lèi)型的初始值identity剿牺,通過(guò)累加器accumulator迭代計(jì)算Stream中的數(shù)據(jù)企垦,得到一個(gè)跟Stream中數(shù)據(jù)相同類(lèi)型的最終結(jié)果。
  • 方式三
    3個(gè)參數(shù)晒来,<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
    reduce的第三個(gè)參數(shù)是在使用parallelStream的reduce操作時(shí)钞诡,合并各個(gè)流結(jié)果的,如果使用的是stream湃崩,所以第三個(gè)參數(shù)是不起作用的荧降。
一或者二參數(shù)的使用示例
        // 1. 求和 SUM 10
        Integer sum = Stream.of(1, 2, 3, 4).reduce(0, (a, b) -> a + b);
        sum = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); //有起始值
        sum = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); //無(wú)起始值
        // 2. 最小值 minValue = -3.0
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double::min).get();
        // 2. 最大數(shù)值 maxValue = 1.0
        double maxValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MIN_VALUE, Double::max);
        maxValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double::max).get();
        // 3. 字符串連接 Concat "ABCD"
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        // 4. 過(guò)濾和字符串連接 Filter & Concat = "ace"
        concat = Stream.of("a", "B", "c", "D", "e", "F")
                .filter(x -> x.compareTo("Z") > 0)
                .reduce("", String::concat);
        //5. Stringbuffer連接
        StringBuffer reduce = target.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).
        reduce(new StringBuffer(), 
              (stringBuffer, stringEntry) -> stringBuffer.append(stringEntry.getKey()).append("=").append(stringEntry.getValue().getClass()).append("&"),
        StringBuffer::append);
三個(gè)參數(shù)進(jìn)行并行計(jì)算reduce高級(jí)用法

需要排序的功能一定不要用并行計(jì)算,因?yàn)椴⑿杏?jì)算為多線程攒读,返回結(jié)果進(jìn)行combine后是亂序的朵诫。

基本用法示例
當(dāng)Stream是并行時(shí),第三個(gè)參數(shù)就有意義了薄扁,它會(huì)將不同線程計(jì)算的結(jié)果調(diào)用combiner做匯總后返回剪返。
注意由于采用了并行計(jì)算废累,前兩個(gè)參數(shù)與非并行時(shí)也有了差異!第一個(gè)參數(shù)如果是并行運(yùn)算脱盲,那么每個(gè)線程都是從這個(gè)初始值開(kāi)始運(yùn)算邑滨,如果初始值不是0或者“”那么與單線程的初始值就有差異了。
舉個(gè)簡(jiǎn)單點(diǎn)的例子钱反,計(jì)算4+1+2+3的結(jié)果掖看,其中4是初始值:

       Integer reduce = Stream.of(1, 2, 3).parallel().reduce(4, (integer, integer2) -> integer + integer2
                , new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                });
        System.out.println("reduce = " + reduce);

并行時(shí)的計(jì)算結(jié)果是18,而非并行時(shí)的計(jì)算結(jié)果是10面哥!
為什么會(huì)這樣哎壳?
先分析下非并行時(shí)的計(jì)算過(guò)程;第一步計(jì)算4 + 1 = 5幢竹,第二步是5 + 2 = 7耳峦,第三步是7 + 3 = 10。
并行計(jì)算時(shí)焕毫,線程之間沒(méi)有影響蹲坷,因此每個(gè)線程在調(diào)用第二個(gè)參數(shù)BiFunction進(jìn)行計(jì)算時(shí),直接都是使用result值當(dāng)其第一個(gè)參數(shù)邑飒,因此計(jì)算過(guò)程現(xiàn)在是這樣的:線程1:1 + 4 = 5循签;線程2:2 + 4 = 6;線程3:3 + 4 = 7疙咸;Combiner函數(shù): 5 + 6 + 7 = 18县匠!

如果初始值是0,那么并行計(jì)算和非并行是結(jié)果一樣的撒轮。

        Integer reduce = Stream.of(1, 2, 3).parallel().reduce(0, (integer, integer2) -> integer + integer2
                , new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                });
        System.out.println("reduce = " + reduce);

輸出reduce = 6

特殊操作示例

  1. 轉(zhuǎn)換成List乞旦,當(dāng)然最簡(jiǎn)單的是collect(Collectors.toList());

說(shuō)明:如果第一個(gè)參數(shù)的類(lèi)型是ArrayList等對(duì)象而非基本數(shù)據(jù)類(lèi)型的包裝類(lèi)或者String,第三個(gè)函數(shù)的處理上可能容易引起誤解题山。下面第三個(gè)參數(shù)(l, s) -> l的入?yún)和s一樣都是第一個(gè)參數(shù)list兰粉,所以這里只返回了l。
按照第三個(gè)參數(shù)作用是:它會(huì)將不同線程計(jì)算的結(jié)果調(diào)用combiner做匯總后返回顶瞳。那就會(huì)這樣寫(xiě)l.addAll(s);這個(gè)時(shí)候出來(lái)的結(jié)果與預(yù)期的結(jié)果就完全不一樣了玖姑,要多了很多元素!

        //parallelStream的并行流慨菱,是多線程的操作焰络,結(jié)果不是按照流的數(shù)據(jù)順序是亂序的。
        ArrayList<String> reduce1 = target.entrySet().parallelStream().map(stringEntry -> stringEntry.getKey()).reduce(new ArrayList<String>(), (l, s) -> {
            l.add(s);
            return l;
        }, (l, s) -> l);
        System.out.println("reduce1 = " + reduce1);

輸出reduce1 = [SELF-TASK, SELF, OTHER-TASK, OTHER]

  1. 第三個(gè)參數(shù)是把各個(gè)流結(jié)果合并為null符喝,由于是parallelStream所以規(guī)則起作用闪彼,返回null。
        //第三個(gè)參數(shù)是把各個(gè)流結(jié)果合并為null协饲,由于是parallelStream所以規(guī)則起作用备蚓,返回null课蔬。
        String reduce2 = target.entrySet().parallelStream().map(stringEntry -> stringEntry.getKey()).reduce("", (r, s) -> r.concat(s).concat(","), (r, s) -> null);
        System.out.println("reduce2 = " + reduce2);

輸出reduce2 = null

  1. 第三個(gè)參數(shù)是把各個(gè)流結(jié)果合并為null,由于是stream不是parallelStream所以規(guī)則不起作用郊尝。
 //第三個(gè)參數(shù)是把各個(gè)流結(jié)果合并為null二跋,由于是stream不是parallelStream所以規(guī)則不起作用。
        val reduce5 = target.entrySet().stream().map(stringEntry -> stringEntry.getKey()).reduce("", (r, s) -> r.concat(s), (r, s) -> null);
        System.out.println("reduce5 = " + reduce5);

輸出reduce5 = OTHERSELF-TASKSELFOTHER-TASK

  1. 初始值為"",進(jìn)行字符串連接流昏,然后進(jìn)行parallelStream()各個(gè)流的再連接扎即。parallelStream所以出來(lái)的結(jié)果是亂序的。
   //初始值為"",進(jìn)行字符串連接况凉,然后進(jìn)行parallelStream()各個(gè)流的再連接
        String reduce4 = target.entrySet().parallelStream().map(stringEntry -> stringEntry.getKey()).reduce("", (r, s) -> r.concat(s).concat(","), String::concat);
        System.out.println("reduce4 = " + reduce4);

輸出reduce4 = OTHER,SELF-TASK,SELF,OTHER-TASK,

  1. 簡(jiǎn)化版的字符串連接示例
  //使用一個(gè)參數(shù)簡(jiǎn)化版的字符串連接谚鄙。
        val reduce3 = target.entrySet().parallelStream().map(stringEntry -> stringEntry.getKey()).reduce(String::concat);
        System.out.println("reduce3 = " + reduce3);

輸出reduce3 = Optional[OTHERSELF-TASKSELFOTHER-TASK]

  1. 迭代stream里面是否包含字符串“SELF”,主要演示在并行流情況下刁绒,reduce處理boolean型數(shù)據(jù)
        //迭代stream里面是否包含字符串“SELF”
        boolean flag = false;
        Boolean f1 = target.entrySet().parallelStream().map(stringEntry -> stringEntry.getKey()).peek(s -> System.out.println("當(dāng)前值:" + s + "闷营,處理的線程:" + Thread.currentThread().getName())).reduce(flag, (f, s) -> {
            f = Boolean.logicalOr(f, s.contains("SELF"));
            System.out.println("當(dāng)前boolean 值f = " + f + ",處理的線程:" + Thread.currentThread().getName());
            return f;
        }, Boolean::logicalOr);
        System.out.println("f1 = " + f1);
  • 第一次運(yùn)行知市,輸出
當(dāng)前值:SELF-TASK傻盟,處理的線程:restartedMain
當(dāng)前值:OTHER-TASK,處理的線程:ForkJoinPool.commonPool-worker-2
當(dāng)前boolean 值f = true嫂丙,處理的線程:restartedMain
當(dāng)前值:OTHER娘赴,處理的線程:ForkJoinPool.commonPool-worker-1
當(dāng)前值:SELF,處理的線程:restartedMain
當(dāng)前boolean 值f = true跟啤,處理的線程:restartedMain
當(dāng)前boolean 值f = true诽表,處理的線程:ForkJoinPool.commonPool-worker-2
當(dāng)前boolean 值f = true,處理的線程:ForkJoinPool.commonPool-worker-1
f1 = true
  • 再一次運(yùn)行隅肥,由于是多線程竿奏,輸出不一樣,但是最后的結(jié)果f1是一樣的腥放。
當(dāng)前值:SELF-TASK泛啸,處理的線程:restartedMain
當(dāng)前boolean 值f = true,處理的線程:restartedMain
當(dāng)前值:SELF捉片,處理的線程:restartedMain
當(dāng)前值:OTHER平痰,處理的線程:ForkJoinPool.commonPool-worker-2
當(dāng)前boolean 值f = true汞舱,處理的線程:restartedMain
當(dāng)前值:OTHER-TASK伍纫,處理的線程:ForkJoinPool.commonPool-worker-1
當(dāng)前boolean 值f = false,處理的線程:ForkJoinPool.commonPool-worker-1
當(dāng)前boolean 值f = false昂芜,處理的線程:ForkJoinPool.commonPool-worker-2
f1 = true

6. 我的一個(gè)實(shí)踐

這個(gè)功能是使用在我微信小程序的后端接口莹规,實(shí)現(xiàn)通過(guò)搜索名字來(lái)展示開(kāi)發(fā)語(yǔ)言列表。


小程序名字“郭秀志”

原代碼

        List<String> filterList = new ArrayList<String>();
        languageList.forEach(c -> {
            if (c.toLowerCase().contains(language_name.toLowerCase()))
                filterList.add("guo"+c);
        });

使用Stream后的代碼

List<String> filterList = languageList.stream().
filter(s -> s.toLowerCase().contains(language_name.toLowerCase())).
map("guo"::concat).
collect(Collectors.toList());

一樣的使用效果
訪問(wèn):https://localhost:8888/v2/vue/api/programLanguage/getByName?language_name=c
返回:["C","C++"]

幾個(gè)重要示例

List的排序泌神、過(guò)濾良漱、peek輸出

@Autowried
List<? extends IBankRouterHandler> source;
List<? extends IBankRouterHandler> collect = source.stream().
                filter(bankHandler -> StringUtils.isNotEmpty(bankHandler.getRouterChannel())).
                sorted(Comparator.comparing(bankHandler -> bankHandler.getRouterChannel())).
                peek(bankHandler -> System.out.println("class為 = " + bankHandler.getRouterChannel())).
                collect(Collectors.toList());

List轉(zhuǎn)Map(把對(duì)象的channel屬性作為key舞虱,對(duì)象本身最為value)

Map<String, ? extends IBankRouterHandler> target = source.stream().
                filter(bankHandler -> StringUtils.isNotEmpty(bankHandler.getRouterChannel())).
                sorted(Comparator.comparing(bankHandler -> bankHandler.getRouterChannel())).
                peek(bankHandler -> System.out.println("class為 = " + bankHandler.getRouterChannel())).
                collect(Collectors.toMap(IBankRouterHandler::getRouterChannel, s -> s));//兩個(gè)參數(shù)分別為Map的key和value。把對(duì)象的channel作為key母市,對(duì)象本身最為value矾兜。

Map排序

Map<String, ? extends IBankRouterHandler> target;
target.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).collect(Collectors.toMap(stringEntry -> stringEntry.getKey(), stringEntry -> stringEntry.getValue()));

Map轉(zhuǎn)List

target.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).map(s -> s.getValue()).collect(Collectors.toList());

forEach

StringBuffer params = new StringBuffer();
target.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach((s) -> params.append(s.getKey() + "=" + s.getValue().getClass() + "&"));
System.out.println("params = " + params.toString());

上面forEach的reduce(StringBuffer)等價(jià)實(shí)現(xiàn)

StringBuffer reduce = target.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).reduce(new StringBuffer(), (stringBuffer, stringEntry) -> stringBuffer.append(stringEntry.getKey()).append("=").append(stringEntry.getValue().getClass()).append("&"), StringBuffer::append);
System.out.println("paramsreduce = " + reduce.toString());

上面forEach的reduce(String)等價(jià)實(shí)現(xiàn)

String reduce1 = target.entrySet().stream().map(stringEntry -> stringEntry.getKey()).filter(key -> key.length() < 11).sorted(Comparator.comparing(s -> s)).reduce("", (s, s2) -> s.concat(s2).concat("&"), String::concat);

上面forEach的collector方法等價(jià)實(shí)現(xiàn)【更簡(jiǎn)單,推薦方法】

Map<String, String> params = new HashMap<String, String>();
        params.put("age", "12");
        params.put("location", "天津");
        params.put("zoo", "cat");
        params.put("name", "guo");
        params.put("go", "school");

        String collect1 = params.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining("&"));
        System.out.println("collect1 = " + collect1);

輸出:collect1 = age=12&go=school&location=天津&name=guo&zoo=cat

自定義比較器在Stream的max患久、min方法

Optional<? extends IBankRouterHandler> max = target.entrySet().stream().map(stringEntry -> stringEntry.getValue()).max((o1, o2) -> o1.getRouterChannel().compareTo(o2.getRouterChannel()));
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椅寺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒋失,更是在濱河造成了極大的恐慌返帕,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篙挽,死亡現(xiàn)場(chǎng)離奇詭異荆萤,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)铣卡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)链韭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人算行,你說(shuō)我怎么就攤上這事梧油。” “怎么了州邢?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵儡陨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我量淌,道長(zhǎng)骗村,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任呀枢,我火速辦了婚禮胚股,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘裙秋。我一直安慰自己牍氛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布皱卓。 她就那樣靜靜地躺著官卡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪枷恕。 梳的紋絲不亂的頭發(fā)上党晋,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼未玻。 笑死灾而,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扳剿。 我是一名探鬼主播旁趟,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庇绽!你這毒婦竟也來(lái)了轻庆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敛劝,失蹤者是張志新(化名)和其女友劉穎余爆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夸盟,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛾方,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了上陕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桩砰。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖释簿,靈堂內(nèi)的尸體忽然破棺而出亚隅,到底是詐尸還是另有隱情,我是刑警寧澤庶溶,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布煮纵,位于F島的核電站,受9級(jí)特大地震影響偏螺,放射性物質(zhì)發(fā)生泄漏行疏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一套像、第九天 我趴在偏房一處隱蔽的房頂上張望酿联。 院中可真熱鬧,春花似錦夺巩、人聲如沸贞让。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)喳张。三九已至,卻和暖如春征绎,著一層夾襖步出監(jiān)牢的瞬間蹲姐,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工人柿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柴墩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓凫岖,卻偏偏與公主長(zhǎng)得像江咳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哥放,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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