JDK Stream流使用介紹

? ? ? ?Stream 是對(duì)集合(Collection)對(duì)象功能的增強(qiáng)蔬芥,它專(zhuān)注于對(duì)集合對(duì)象進(jìn)行各種非常便利梆靖、高效的聚合操作,大批量數(shù)據(jù)操作笔诵。通常我們需要多行代碼才能完成的操作返吻,借助于Stream流式處理可以很簡(jiǎn)單的實(shí)現(xiàn)。

? ? ? ?Stream 不是集合元素乎婿,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù)测僵,它是有關(guān)算法和計(jì)算的一個(gè)對(duì)象,它更像一個(gè)高級(jí)版本的Iterator次酌。同時(shí)Stream提供串行和并行兩種模式進(jìn)行匯聚操作恨课。比如你的Stream里面有很多數(shù)據(jù),Stream可以開(kāi)多個(gè)線程每個(gè)線程處理一部分岳服。最后把結(jié)果匯總起來(lái)剂公。

? ? ? ?在開(kāi)始之前我們先用一個(gè)圖來(lái)整體的概況下Stream。如下所示:

? ? ? ?

一 Stream流創(chuàng)建

? ? ? ?想使用Stream流吊宋,首先咱得先創(chuàng)建一個(gè)Stream流對(duì)象纲辽。創(chuàng)建Steam需要數(shù)據(jù)源.這些數(shù)據(jù)源可以是集合、可以是數(shù)組、可以使文件拖吼、甚至是你可以去自定義等等鳞上。

1.1 集合作為Stream數(shù)據(jù)源

? ? ? ?集合Collection作為Stream的數(shù)據(jù)源,應(yīng)該也是我們用的最多的一種數(shù)據(jù)源了吊档。Collection里面也提供了一些方法幫助我們把集合Collection轉(zhuǎn)換成Stream篙议。

1.1.1 stream()方法

? ? ? ?調(diào)用Collection.stream()函數(shù)創(chuàng)建一個(gè)Stream對(duì)象。相當(dāng)于把集合Collection里面的數(shù)據(jù)都導(dǎo)入到了Stream里面去了怠硼。

    @Test
    public void collectionStream() {

        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        // 使用List創(chuàng)建一個(gè)流對(duì)象
        Stream<Integer> stream = list.stream();
        // TODO: 對(duì)流對(duì)象做處理
    }

1.1.2 parallelStream()方法

? ? ? ?調(diào)用Collection.parallelStream()創(chuàng)建Stream對(duì)象鬼贱。

    @Test
    public void collectionParallelStream() {

        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        // 使用List創(chuàng)建一個(gè)流對(duì)象
        Stream<Integer> stream = list.parallelStream();
        // TODO: 對(duì)流對(duì)象做處理
    }

parallelStream()與stream()區(qū)別是parallelStream()使用多線程并發(fā)處理最后匯總結(jié)果,而stream()是單線程香璃。所以相對(duì)來(lái)說(shuō)parallelStream()效率要稍微高點(diǎn)这难。

1.2 數(shù)組作為Stream數(shù)據(jù)源

? ? ? ?數(shù)組也可以作為Stream的數(shù)據(jù)源。我們可以通過(guò)Arrays.stream()方法把一個(gè)數(shù)組轉(zhuǎn)化成流對(duì)象葡秒。Arrays.stream()方法很豐富姻乓,有很多個(gè)。大家可以根據(jù)實(shí)際情況使用眯牧。

    @Test
    public void arrayStream() {

        int[] intArray = new int[10];
        for (int index = 0; index < intArray.length; index++) {
            intArray[index] = index;
        }
        // 使用數(shù)組創(chuàng)建一個(gè)流對(duì)象
        IntStream stream = Arrays.stream(intArray);
        // TODO: 對(duì)流對(duì)象做處理
    }

1.3 BufferedReader作為Stream數(shù)據(jù)源

? ? ? ?我們也可以把BufferedReader里面lines方法把BufferedReader里面每一行的數(shù)據(jù)作為數(shù)據(jù)源生成一個(gè)Stream對(duì)象蹋岩。

    @Test
    public void bufferedReaderStream() {

        File file = new File("/home/tuacy/github/google-guava-study/src/main/resources/application.yml");
        try {
            // 把文件里面的內(nèi)容一行一行的讀出來(lái)
            BufferedReader in = new BufferedReader(new FileReader(file));
            // 生成一個(gè)Stream對(duì)象
            Stream<String> stream = in.lines();
            // TODO: 對(duì)流對(duì)象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4 File作為Stream數(shù)據(jù)源

? ? ? ?Files里面多個(gè)生成Stream對(duì)象的方法,都是對(duì)Path(文件)的操作炸站。有的是指定Path目錄下所有的子文件(所有的子文件相當(dāng)于是一個(gè)列表了)作為Stream數(shù)據(jù)源星澳,有的把指定Path文件里面的每一行數(shù)據(jù)作為Stream的數(shù)據(jù)源。

1.4.1 Files.list()

? ? ? ?列出指定Path下面的所有文件旱易。把這些文件作為Stream數(shù)據(jù)源禁偎。

    @Test
    public void fileListStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 找到指定path下的所有的文件
            Stream<Path> stream = Files.list(path);
            // TODO: 對(duì)流對(duì)象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.2 Files.walk()

? ? ? ?Files.walk()方法用于遍歷子文件(包括文件夾)。參數(shù)maxDepth用于指定遍歷的深度阀坏。把子文件(子文件夾)作為Stream數(shù)據(jù)源如暖。

    @Test
    public void fileWalkStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 第二個(gè)參數(shù)用于指定遍歷幾層
            Stream<Path> stream = Files.walk(path, 2);
            // TODO: 對(duì)流對(duì)象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.3 Files.find()

? ? ? ?Files.find方法用于遍歷查找(過(guò)濾)子文件。參數(shù)里面會(huì)指定查詢(過(guò)濾)條件忌堂。把過(guò)濾出來(lái)的子文件作為Stream的數(shù)據(jù)源盒至。

    @Test
    public void fileFindStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 找到指定path下的所有不是目錄的文件
           Stream<Path> stream = Files.find(path, 2, (path1, basicFileAttributes) -> {
               // 過(guò)濾掉目錄文件
               return !basicFileAttributes.isDirectory();
           });
            // TODO: 對(duì)流對(duì)象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.4 Files.lines()

? ? ? ?Files.lines方法是把指定Path文件里面的每一行內(nèi)容作為Stream的數(shù)據(jù)源。

    @Test
    public void fileLineStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources\\application.yml");
        try {
            // 生成一個(gè)Stream對(duì)象
            Stream<String> stream = Files.lines(path);
            // TODO: 對(duì)流對(duì)象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.5 自己構(gòu)建Stream

? ? ? ?我們也可以自己去創(chuàng)建Stream自己提供數(shù)據(jù)源士修。Stream類(lèi)里面提供of()枷遂、iterate()、generate()棋嘲、builder()等一些方法來(lái)創(chuàng)建Stream酒唉,Stream的數(shù)據(jù)源我們自己提供。

1.5.1 Stream.of()

? ? ? ?Stream.of()函數(shù)參數(shù)就是數(shù)據(jù)源沸移。

       Stream<Integer> ofSteam = Stream.of(1,2,3,4,5,6);

1.5.2 Stream.iterate()

? ? ? ?Stream.iterate()可以用來(lái)生成無(wú)限流痪伦,函數(shù)需要兩個(gè)參數(shù):第一個(gè)參數(shù)是初始值侄榴、第二個(gè)參數(shù)用于確定怎么根據(jù)前一個(gè)元素的值生成下一個(gè)元素。

        // Stream.iterate() 流式迭代器 Stream.iterate()函數(shù)的第二個(gè)參數(shù)告訴你怎么去生成下一個(gè)元素
        Stream<BigInteger> integers = Stream.iterate(
                BigInteger.ONE,
                new UnaryOperator<BigInteger>() {

                    @Override
                    public BigInteger apply(BigInteger bigInteger) {
                        return bigInteger.add(BigInteger.ONE);
                    }
                });
        // 簡(jiǎn)單輸出
        integers.limit(10).forEach(new Consumer<BigInteger>() {
            @Override
            public void accept(BigInteger bigInteger) {
                System.out.println(bigInteger.intValue());
            }
        });

1.5.3 Stream.generate()

? ? ? ?Stream.generate()也是用于生成一個(gè)無(wú)限流网沾。參數(shù)用于獲取每個(gè)元素癞蚕。

        // Stream.generate() 生成無(wú)限流
        Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
            @Override
            public Double get() {
                return java.lang.Math.random() * 100;
            }
        });
        // 簡(jiǎn)單輸出前10個(gè)值
        generateA.limit(10).forEach(new Consumer<Double>() {
            @Override
            public void accept(Double bigInteger) {
                System.out.println(bigInteger.intValue());
            }
        });

1.5.4 Stream.build()

? ? ? ?Stream.build()通過(guò)建造者模式生成一個(gè)Stream建造器。然后把需要加入Stream里面的數(shù)據(jù)源一個(gè)一個(gè)通過(guò)建造器添加進(jìn)去辉哥。

        // Stream.builder()構(gòu)造一個(gè)Stream對(duì)象
        Stream.Builder<Integer> build = Stream.<Integer>builder().add(1)
                .add(2)
                .add(3);
        build.accept(4);
        build.accept(5);
        build.build().forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
        // TODO: 對(duì)流對(duì)象做處理

1.6 其他Stream創(chuàng)建方式

? ? ? ?Stream其他創(chuàng)建方式我們就不一一舉例了桦山。有如下方式。

  • Random.ints()证薇。
  • BitSet.stream()度苔。
  • Pattern.splitAsStream(java.lang.CharSequence)
  • JarFile.stream()。
  • ...

二 Stream流操作(中間操作符)

什么是操作符浑度?操作符就是對(duì)數(shù)據(jù)進(jìn)行的一種處理工作,一道加工程序鸦概;就好像工廠的工人對(duì)流水線上的產(chǎn)品進(jìn)行一道加工程序一樣箩张。

? ? ? ?Stream流操作就是對(duì)Stream流的各種處理。Stream里面已經(jīng)給提供了很多中間操作(我們一般稱之為操作符)窗市。

Stream提供的流操作符先慷。

Stream流操作符 解釋
filter 過(guò)濾
map 對(duì)流里面每個(gè)元素做轉(zhuǎn)換
mapToInt 把流里面的每個(gè)元素轉(zhuǎn)換成int
mapToLong 流里面每個(gè)元素轉(zhuǎn)換成long
mapToDouble 流里面每個(gè)元素轉(zhuǎn)換成double
flatMap 流里面每個(gè)元素轉(zhuǎn)換成Steam對(duì)象,最后平鋪成一個(gè)Stream對(duì)象
flatMapToInt 流里面每個(gè)元素轉(zhuǎn)換成IntStream對(duì)象咨察,最后平鋪成一個(gè)IntStream對(duì)象
flatMapToLong 流里面每個(gè)元素轉(zhuǎn)換成LongStream對(duì)象论熙,最后平鋪成一個(gè)LongStream對(duì)象
flatMapToDouble 流里面每個(gè)元素轉(zhuǎn)換成DoubleStream對(duì)象,最后平鋪成一個(gè)DoubleStream對(duì)象
distinct 去重
sorted 排序
peek 查看流里面的每個(gè)元素
limit 返回前n個(gè)數(shù)
skip 跳過(guò)前n個(gè)元素

? ? ? ?Stream提供了這么多的操作符摄狱,而且這些操作符是可以組合起來(lái)使用脓诡。關(guān)于每個(gè)操作符的使用我們用一個(gè)簡(jiǎn)單的實(shí)例代碼來(lái)說(shuō)明。

2.1 filter

? ? ? ?filter用于對(duì)流里面的數(shù)據(jù)做過(guò)濾操作媒役。

    // 過(guò)濾
    @Test
    public void filter() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 過(guò)濾出偶數(shù)
        Stream<Integer> filterStream = stream.filter(new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer % 2 == 0;
            }
        });
        // 簡(jiǎn)單輸出
        filterStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.2 map

? ? ? ?map用于對(duì)流里面的元素做轉(zhuǎn)換祝谚。

    // 轉(zhuǎn)換
    @Test
    public void map() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 整數(shù)轉(zhuǎn)換為String
        Stream<String> mapStream = stream.map(new Function<Integer, String>(){
            @Override
            public String apply(Integer integer) {
                return String.valueOf(integer);
            }
        });
        // 簡(jiǎn)單輸出
        mapStream.forEach(new Consumer<String>() {
            @Override
            public void accept(String integer) {
                System.out.println(integer);
            }
        });
    }

2.3 mapToInt、mapToLong酣衷、mapToDouble

? ? ? ?mapToInt交惯、mapToLong、mapToDouble用于將流里面的元素轉(zhuǎn)換成對(duì)應(yīng)的類(lèi)型穿仪。

    // 轉(zhuǎn)換
    @Test
    public void mapToInt() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 整數(shù)轉(zhuǎn)換為String
        IntStream mapStream = stream.mapToInt(new ToIntFunction<Integer>(){

            @Override
            public int applyAsInt(Integer value) {
                return value == null ? 0 : value;
            }
        });
        // 簡(jiǎn)單輸出總和
        System.out.println(mapStream.sum());
    }

2.4 flatMap席爽、flatMapToInt、flatMapToLong啊片、flatMapToDouble

? ? ? ?flatMap只锻、flatMapToInt、flatMapToLong钠龙、flatMapToDouble也是對(duì)每個(gè)元素的轉(zhuǎn)換炬藤,不過(guò)他們和map的不同點(diǎn)在于御铃,他們是吧每個(gè)元素轉(zhuǎn)換成一個(gè)Stream流,最終在平鋪成一個(gè)Stream流沈矿。

    // 轉(zhuǎn)換
    @Test
    public void flatMap() {
        Stream<String> stream = Stream.of("java:1", "android:2", "ios:3");
        // 整數(shù)轉(zhuǎn)換為String
        Stream<String> rerStream = stream.flatMap(
                new Function<String, Stream<String>>() {
                    @Override
                    public Stream<String> apply(String s) {
                        // 分割
                        Iterable<String> iterableList = Splitter.on(':').trimResults() // 移除前面和后面的空白
                                .omitEmptyStrings()
                                .split(s);
                        return Lists.newArrayList(iterableList).parallelStream();
                    }
                });
        // 簡(jiǎn)單輸出
        rerStream.forEach(new Consumer<String>() {
            @Override
            public void accept(String integer) {
                System.out.println(integer);
            }
        });
    }
    // 轉(zhuǎn)換
    @Test
    public void reduce() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 所有的元素相加上真,在加上20
        Integer reduceValue = stream.reduce(20, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                System.out.println(integer);
                System.out.println(integer2);
                return integer + integer2;
            }
        });
        System.out.println(reduceValue);
    }

2.5 distinct

? ? ? ?distinct操作符用于去重。

    // 去重
    @Test
    public void distinct() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        Stream<Integer> rerStream = stream.distinct();
        // 簡(jiǎn)單輸出
        rerStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }
    distinct根據(jù)某個(gè)字段去重羹膳。

2.6 sorted

? ? ? ?sorted操作符用于對(duì)流里面的元素排序睡互。

    // 排序
    @Test
    public void sorted() {
        Stream<Integer> stream = Stream.of(1,2,3,2,5,4,8,6);
        Stream<Integer> rerStream = stream.sorted(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                if (o1.equals(o2)) {
                    return 0;
                } else {
                    return o1 > o2 ? 1 : -1;
                }
            }
        });
        // 簡(jiǎn)單輸出
        rerStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.7 peek

? ? ? ?peek操作符用于查看流里面的每個(gè)元素。在多個(gè)操作符同時(shí)使用的時(shí)候的中間加入peek操作符可以參考每個(gè)操作符之后的結(jié)果陵像。

    // 查看
    @Test
    public void peek() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        // 查看
        Stream<Integer> reStream = stream.peek(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
        // 簡(jiǎn)單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.8 limit

? ? ? ?limit操作符用于取流前面多少個(gè)元素就珠。

    // limit
    @Test
    public void limit() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        // limit
        Stream<Integer> reStream = stream.limit(3);
        // 簡(jiǎn)單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.9 skip

? ? ? ?skip用于跳過(guò)里面的多少個(gè)元素。

    // skip
    @Test
    public void skip() {
        Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
        // skip
        Stream<Integer> reStream = stream.skip(3);
        // 簡(jiǎn)單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

三. Stream流終端操作(終端操作符)

? ? ? ?Stream流終端操作是流式處理的最后一步醒颖,之前已經(jīng)對(duì)Stream做了一系列的處理之后妻怎。該拿出結(jié)果了。我們可以在終端操作中實(shí)現(xiàn)對(duì)流的遍歷泞歉、查找逼侦、歸約、收集等等一系列的操作腰耙。

Stream流終端操作提供的函數(shù)有

終端操作符 解釋
forEach 遍歷
forEachOrdered 如果流里面的元素是有順序的則按順序遍歷
toArray 轉(zhuǎn)換成數(shù)組
reduce 歸約 - 根據(jù)一定的規(guī)則將Stream中的元素進(jìn)行計(jì)算后返回一個(gè)唯一的值
collect 收集 - 對(duì)處理結(jié)果的封裝
min 最小值
max 最大值
count 元素的個(gè)數(shù)
anyMatch 任何一個(gè)匹配到了就返回true
allMatch 所有都匹配上了就返回true
noneMatch 沒(méi)有一個(gè)匹配上就返回true
findFirst 返回滿足條件的第一個(gè)元素
findAny 返回某個(gè)元素

? ? ? ?關(guān)于Stream終端操作部分榛丢,我們就著重講下collect()函數(shù)的使用。因?yàn)槠渌慕K端操作符都很好理解挺庞。collect()稍稍復(fù)雜一點(diǎn)晰赞。

3.1 collect()

? ? ? ?collect()的使用主要在于對(duì)參數(shù)的理解,所有我們這里要專(zhuān)門(mén)講下collect()函數(shù)的參數(shù)Collector這個(gè)類(lèi)选侨,以及怎么去構(gòu)建Collector對(duì)象掖鱼。只有在了解了這些之后,咱們才可以熟練的把他們用在各種場(chǎng)景中侵俗。

3.1.1 Collector

? ? ? ?Collector類(lèi)目前沒(méi)別的用處锨用,就是專(zhuān)門(mén)用來(lái)作為Stream的collect()方法的參數(shù)的。把Stream里面的數(shù)據(jù)轉(zhuǎn)換成我們最終想要的結(jié)果上隘谣。

Collector各個(gè)方法增拥,以及每個(gè)泛型的介紹

/**
 * Collector是專(zhuān)門(mén)用來(lái)作為Stream的collect方法的參數(shù)的
 *
 * 泛型含義
 * T:是流中要收集的對(duì)象的泛型
 * A:是累加器的類(lèi)型,累加器是在收集過(guò)程中用于累積部分結(jié)果的對(duì)象寻歧。
 * R:是收集操作得到的對(duì)象(通常但不一定是集合)的類(lèi)型掌栅。
 */
public interface Collector<T, A, R> {
    /**
     * 生成結(jié)果容器,容器類(lèi)型為A
     * (多線程的情況下可能會(huì)調(diào)用多次,開(kāi)多個(gè)線程同時(shí)去處理一個(gè)流码泛,每個(gè)線程調(diào)用一次)
     */
    Supplier<A> supplier();

    /**
     * A對(duì)應(yīng)supplier()函數(shù)創(chuàng)建的結(jié)果容器
     * T對(duì)應(yīng)Stream流里面一個(gè)一個(gè)的元素
     * 用于消費(fèi)元素猾封,也就是歸納元素,一般在這個(gè)里面把流里面的元素T(也可以轉(zhuǎn)換下)放到supplier()創(chuàng)建的結(jié)果T里面去
     */
    BiConsumer<A, T> accumulator();

    /**
     * 用于兩個(gè)兩個(gè)合并并行執(zhí)行的線程的執(zhí)行結(jié)果噪珊,將其合并為一個(gè)最終結(jié)果A
     * 多線程的情況下晌缘,多個(gè)線程并行執(zhí)行齐莲。每個(gè)線程產(chǎn)生一個(gè)結(jié)果
     */
    BinaryOperator<A> combiner();

    /**
     * 用于將之前整合完的結(jié)果A轉(zhuǎn)換成為R
     *
     * combiner()完成之后了A, 這里還可以在轉(zhuǎn)一道。生成你自己想要的結(jié)果
     */
    Function<A, R> finisher();

    /**
     * characteristics表示當(dāng)前Collector的特征值磷箕,
     * 這是個(gè)不可變Set
     * 它定義了收集器的行為--尤其是關(guān)于流是否可以多線程并行執(zhí)行选酗,以及可以使用哪些優(yōu)化的提示
     */
    Set<Characteristics> characteristics();

    /**
     * 它定義了收集器的行為--尤其是關(guān)于流是否可以并行歸約,以及可以使用哪些優(yōu)化的提示
     */
    enum Characteristics {
        /**
         * accumulator函數(shù)可以從多個(gè)線程同時(shí)調(diào)用岳枷,且該收集器可以并行歸約流披泪。如果收集器沒(méi)有標(biāo)為UNORDERED,
         * 那它僅在用于無(wú)序數(shù)據(jù)源時(shí)才可以并行歸約
         * 多線程并行
         */
        CONCURRENT,

        /**
         * 歸約結(jié)果不受流中項(xiàng)目的遍歷和累積順序的影響(無(wú)序)
         */
        UNORDERED,

        /**
         * 無(wú)需轉(zhuǎn)換結(jié)果
         */
        IDENTITY_FINISH
    }


    /**
     * 四參方法括袒,用于生成一個(gè)Collector,T代表流中的一個(gè)一個(gè)元素蚜锨,R代表最終的結(jié)果
     */
    public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
                                              BiConsumer<R, T> accumulator,
                                              BinaryOperator<R> combiner,
                                              Characteristics... characteristics);

    /**
     * 五參方法幔妨,用于生成一個(gè)Collector相恃,T代表流中的一個(gè)一個(gè)元素奋早,A代表中間結(jié)果夷蚊,R代表最終結(jié)果,finisher用于將A轉(zhuǎn)換為R
     */
    public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
                                                 BiConsumer<A, T> accumulator,
                                                 BinaryOperator<A> combiner,
                                                 Function<A, R> finisher,
                                                 Characteristics... characteristics);



}

? ? ? ?有了上面的介紹饭于,接下來(lái)我們自己來(lái)new一個(gè)Collector對(duì)象蜀踏,把我們Steam流里面的數(shù)據(jù)轉(zhuǎn)換成List。(當(dāng)然了Collectors類(lèi)里面有提供這個(gè)方法掰吕,這里我們自己寫(xiě)一個(gè)也是為了方便大家的理解)

    // 自己來(lái)組裝Collector,返回一個(gè)List
    @Test
    public void collectNew() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> intList = stream.collect(
                new Collector<Integer, List<Integer>, List<Integer>>() {
                    // 生成結(jié)果容器颅痊,容器類(lèi)型為,我們這里為L(zhǎng)ist<Integer>
                    @Override
                    public Supplier<List<Integer>> supplier() {
                        return new Supplier<List<Integer>>() {

                            @Override
                            public List<Integer> get() {
                                return new ArrayList<>();
                            }
                        };
                    }

                    // 把流里面的結(jié)果都放到結(jié)果容器里面去
                    @Override
                    public BiConsumer<List<Integer>, Integer> accumulator() {
                        return new BiConsumer<List<Integer>, Integer>() {
                            @Override
                            public void accept(List<Integer> integers, Integer integer) {
                                integers.add(integer);
                            }
                        };
                    }

                    // 兩個(gè)兩個(gè)合并并行執(zhí)行的線程的執(zhí)行結(jié)果殖熟,將其合并為一個(gè)最終結(jié)果A
                    @Override
                    public BinaryOperator<List<Integer>> combiner() {
                        return new BinaryOperator<List<Integer>>() {
                            @Override
                            public List<Integer> apply(List<Integer> left, List<Integer> right) {
                                left.addAll(right);
                                return left;
                            }
                        };
                    }

                    // 可以對(duì)最終的結(jié)果做一個(gè)轉(zhuǎn)換操作
                    @Override
                    public Function<List<Integer>, List<Integer>> finisher() {
                        return new Function<List<Integer>, List<Integer>>() {
                            @Override
                            public List<Integer> apply(List<Integer> integers) {
                                return integers;
                            }
                        };
                    }

                    // 特征值
                    @Override
                    public Set<Characteristics> characteristics() {
                        return EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH);
                    }
                });

        for (Integer item : intList) {
            System.out.println(item);
        }
    }

3.1.2 Collectors

? ? ? ?Collectors是Collector的工具類(lèi),用來(lái)創(chuàng)建Collector對(duì)象斑响。它內(nèi)部已經(jīng)給我們提供了各種各樣的創(chuàng)建Collector對(duì)象的方法菱属,已經(jīng)能滿足我們大部分的需求了,我們可以直接拿來(lái)使用舰罚,非常方便纽门。

Collectors各個(gè)方法介紹

public final class Collectors {


    /**
     * 將流中的元素全部放置到一個(gè)集合中返回
     *
     * collectionFactory參數(shù)用于創(chuàng)建Collection對(duì)象(比如List,LinkeList等等)
     */
    public static <T, C extends Collection<T>>
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);

    /**
     * 將流中的元素放置到一個(gè)列表集合中去。這個(gè)列表默認(rèn)為ArrayList
     */
    public static <T>
    Collector<T, ?, List<T>> toList();

    /**
     * 將流中的元素放置到一個(gè)無(wú)序集set中去营罢。默認(rèn)為HashSet
     */
    public static <T>
    Collector<T, ?, Set<T>> toSet();

    /**
     * joining的目的是將流中的元素全部以字符序列的方式連接到一起赏陵,可以指定連接符,甚至是結(jié)果的前后綴
     */
    public static Collector<CharSequence, ?, String> joining();
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix);

    /**
     * 這個(gè)映射是首先對(duì)流中的每個(gè)元素進(jìn)行映射饲漾,即類(lèi)型轉(zhuǎn)換蝙搔,然后再將新元素以給定的Collector進(jìn)行歸納
     */
    public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream);

    /**
     * 該方法是在歸納動(dòng)作結(jié)束之后,對(duì)歸納的結(jié)果進(jìn)行再處理
     */
    public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                                Function<R,RR> finisher);

    /**
     * 該方法用于計(jì)數(shù)
     */
    public static <T> Collector<T, ?, Long> counting();

    /**
     * 生成一個(gè)用于獲取最小/最大值的Optional結(jié)果的Collector
     */
    public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator);
    public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator);

    /**
     * 生成一個(gè)用于求元素和的Collector考传,首先通過(guò)給定的mapper將元素轉(zhuǎn)換類(lèi)型吃型,然后再求和
     */
    public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper);
    public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper);
    public static <T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper);

    /**
     * 生成一個(gè)用于求元素平均值的Collector,首選通過(guò)參數(shù)將元素轉(zhuǎn)換為指定的類(lèi)型
     */
    public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper);
    public static <T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper);
    public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper);

    /**
     * 規(guī)約 對(duì)流中的元素做統(tǒng)計(jì)歸納作用
     * 和Stream類(lèi)里面的reducing操作符一樣
     */
    public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op);
    public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op);
    public static <T, U> Collector<T, ?, U> reducing(U identity,
                                Function<? super T, ? extends U> mapper,
                                BinaryOperator<U> op);

    /**
     * 這個(gè)方法是用于生成一個(gè)擁有分組功能的Collector
     */
    public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier);
    public static <T, K, A, D>
    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream);
    public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream);

    /**
     * 和groupingBy方法一樣僚楞,只是返回的Collector是并行的
     */
    public static <T, K>
    Collector<T, ?, ConcurrentMap<K, List<T>>>
    groupingByConcurrent(Function<? super T, ? extends K> classifier);
    public static <T, K, A, D>
    Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
                                                              Collector<? super T, A, D> downstream);
    public static <T, K, A, D, M extends ConcurrentMap<K, D>>
    Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
                                            Supplier<M> mapFactory,
                                            Collector<? super T, A, D> downstream);

    /**
     * 該方法將流中的元素按照給定的校驗(yàn)規(guī)則的結(jié)果分為兩個(gè)部分勤晚,放到一個(gè)map中返回枉层,map的鍵是Boolean類(lèi)型,值為元素的列表List赐写。
     */
    public static <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate);
    public static <T, D, A>
    Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
                                                    Collector<? super T, A, D> downstream);

    /**
     * 根據(jù)給定的鍵生成器和值生成器生成的鍵和值保存到一個(gè)map中返回鸟蜡,鍵和值的生成都依賴于元素,
     * 可以指定出現(xiàn)重復(fù)鍵時(shí)的處理方案和保存結(jié)果的map
     */
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper);
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction);
    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                             Function<? super T, ? extends U> valueMapper,
                             BinaryOperator<U> mergeFunction,
                             Supplier<M> mapSupplier);

    /**
     * 并發(fā)版本的toMap
     */
    public static <T, K, U>
    Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
                                                        Function<? super T, ? extends U> valueMapper);

    public static <T, K, U>
    Collector<T, ?, ConcurrentMap<K,U>>
    toConcurrentMap(Function<? super T, ? extends K> keyMapper,
                    Function<? super T, ? extends U> valueMapper,
                    BinaryOperator<U> mergeFunction);

    public static <T, K, U, M extends ConcurrentMap<K, U>>
    Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
                                       Function<? super T, ? extends U> valueMapper,
                                       BinaryOperator<U> mergeFunction,
                                       Supplier<M> mapSupplier);

    /**
     * 這三個(gè)方法適用于匯總的血淌,返回值分別是IntSummaryStatistics矩欠,LongSummaryStatistics,DoubleSummaryStatistics
     */
    public static <T>
    Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper);
    
    public static <T>
    Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper);
    
    public static <T>
    Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper);
}

? ? ? ?為了加深大家的理解悠夯。接下來(lái)我們對(duì)Collectors里面的每個(gè)函數(shù)都寫(xiě)一個(gè)簡(jiǎn)單的實(shí)例癌淮。

3.1.2.1 toCollection

? ? ? ?將流中的元素全部放置到一個(gè)集合中返回,這里使用Collection沦补,泛指多種集合乳蓄。

    @Test
    public void toCollection() {
        List<String> list = Arrays.asList("java", "ios", "c");
        LinkedList<String> retList = list.stream().collect(Collectors.toCollection(
                new Supplier<LinkedList<String>>() {

                    @Override
                    public LinkedList<String> get() {
                        return new LinkedList<>();
                    }
                }));
    }

3.1.2.2 toList

? ? ? ?將流中的元素放置到一個(gè)列表集合中去。這個(gè)列表默認(rèn)為ArrayList夕膀。

    @Test
    public void collectList() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> intList = stream.collect(Collectors.toList());
    }

3.1.2.3 toSet

? ? ? ?將流中的元素放置到一個(gè)無(wú)序集set中去虚倒。默認(rèn)為HashSet。

    @Test
    public void toSet() {
        List<String> list = Arrays.asList("java", "ios", "c");
        Set<String> retList = list.stream().collect(Collectors.toSet());
    }

3.1.2.4 joining

? ? ? ?joining的目的是將流中的元素全部以字符序列的方式連接到一起产舞,可以指定連接符魂奥,甚至是結(jié)果的前后綴。

    @Test
    public void joining() {
        List<String> list = Arrays.asList("java", "ios", "c");
        String ret = list.stream().collect(Collectors.joining(":", "@@", "@@"));
        System.out.println(ret);//@@java:ios:c@@
    }

3.1.2.5 mapping

? ? ? ?這個(gè)映射是首先對(duì)流中的每個(gè)元素進(jìn)行映射易猫,即類(lèi)型轉(zhuǎn)換耻煤,然后再將新元素以給定的Collector進(jìn)行歸納。

    @Test
    public void mapping() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 先把流里面的每個(gè)元素的前后加上[],之后在用:拼接起來(lái)
        String ret = list.stream().collect(Collectors.mapping(
                new Function<String, String>() {
                    @Override
                    public String apply(String s) {
                        return "[" + s + "]";
                    }
                },
                Collectors.joining(":")));
        System.out.println(ret);//[java]:[ios]:[c]
    }

3.1.2.6 collectingAndThen

? ? ? ?該方法是在歸納動(dòng)作結(jié)束之后准颓,對(duì)歸納的結(jié)果進(jìn)行再處理哈蝇。

    @Test
    public void collectingAndThen() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // Collectors.toList()之后在把List<String>通過(guò)Joiner轉(zhuǎn)換String
        String ret = list.stream().collect(Collectors.collectingAndThen(
                Collectors.toList(),
                new Function<List<String>, String>() {
                    @Override
                    public String apply(List<String> strings) {
                        return Joiner.on("; ")
                                .join(strings);
                    }
                }));
        System.out.println(ret);//java; ios; c
    }
    關(guān)于collectingAndThen有幾個(gè)在編碼過(guò)程經(jīng)常用到的場(chǎng)景,根據(jù)list中對(duì)象的屬性去重攘已。
    @Test
    public void collectingAndThen() {
        List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
        List<Student> ret = list.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getAge))), ArrayList::new
                )
        );

        ret.forEach(new Consumer<Student>() {
            @Override
            public void accept(Student student) {
                System.out.println(student.toString());
            }
        });
    }
    
private static class Student {
        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

3.1.2.7 counting

? ? ? ?計(jì)算流里面的元素個(gè)數(shù)炮赦。

    @Test
    public void counting() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 元素個(gè)數(shù)
        Long ret = list.stream().collect(Collectors.counting());
        System.out.println(ret);//3
    }

3.1.2.8 minBy/maxBy

? ? ? ?生成一個(gè)用于獲取最小/最大值的Optional結(jié)果的Collector。

    @Test
    public void minBy() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 這里簡(jiǎn)單的用字符串比較的大小
        Optional<String> ret = list.stream().collect(Collectors.minBy(
                new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        return o1.compareTo(o2);
                    }
                }));
    }

3.2.2.9 summingInt/summingLong/summingDouble

? ? ? ?生成一個(gè)用于求元素和的Collector样勃,首先通過(guò)給定的mapper將元素轉(zhuǎn)換類(lèi)型吠勘,然后再求和。

    @Test
    public void summingInt() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求和
        Integer ret = list.stream().collect(Collectors.summingInt(
                new ToIntFunction<Integer>() {
                    @Override
                    public int applyAsInt(Integer value) {
                        return value;
                    }

                }));
    }

3.2.2.10 averagingInt/averagingLong/averagingDouble

? ? ? ?生成一個(gè)用于求元素平均值的Collector彤灶,首選通過(guò)參數(shù)將元素轉(zhuǎn)換為指定的類(lèi)型看幼。

    @Test
    public void averagingInt() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求平均值
        Double ret = list.stream().collect(Collectors.averagingInt(
                new ToIntFunction<Integer>() {
                    @Override
                    public int applyAsInt(Integer value) {
                        return value;
                    }

                }));
    }

3.1.2.11 reducing

? ? ? ?reducing方法有三個(gè)重載方法,其實(shí)是和Stream里的三個(gè)reduce方法對(duì)應(yīng)的幌陕,二者是可以替換使用的诵姜,作用完全一致,也是對(duì)流中的元素做統(tǒng)計(jì)歸納作用(規(guī)約)。

    @Test
    public void reducing() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求平均值
        Integer ret = list.stream().collect(Collectors.reducing(
                10,
                new Function<Integer, Integer>() {
                    @Override
                    public Integer apply(Integer integer) {
                        return integer * integer;
                    }
                },
                new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                }));

        System.out.println(ret); // 10 + 1*1 + 2*2 + 3*3 = 24
    }

3.1.2.12 groupingBy

? ? ? ?這個(gè)方法是用于生成一個(gè)擁有分組功能的Collector棚唆,它也有三個(gè)重載方法暇赤。這里要稍微解釋下參數(shù)最長(zhǎng)的函數(shù)每個(gè)參數(shù)的含義。

  • Function<? super T, ? extends K> classifier:確定怎么從流里面每個(gè)元素獲取key宵凌。
  • Supplier<M> mapFactory:用于創(chuàng)建最終要生成的對(duì)象一般都是Map鞋囊。
  • Collector<? super T, A, D> downstream:每一組數(shù)據(jù)用什么來(lái)存放,一般是List瞎惫,所以這個(gè)參數(shù)一般是Collectors.toList()溜腐。
    @Test
    public void collectGroupingBy() {
        List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
        Map<Integer, List<Student>> ret = list.stream().collect(Collectors.groupingBy(
                new Function<Student, Integer>() {
                    @Override
                    public Integer apply(Student student) {
                        return student.getAge();
                    }
                },
                new Supplier<Map<Integer, List<Student>>>() {
                    @Override
                    public Map<Integer, List<Student>> get() {
                        return new HashMap<>();
                    }
                },
                Collectors.toList()));

        for (Map.Entry<Integer, List<Student>> entry : ret.entrySet()) {
            Integer mapKey = entry.getKey();
            List<Student> mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }

    }


    private static class Student {
        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

groupingByConcurrent和groupingBy是一樣的,只不過(guò)groupingByConcurrent生成的是ConcurrentMap瓜喇,而groupingBy生成的是Map挺益。

3.1.2.13 partitioningBy

? ? ? ?該方法將流中的元素按照給定的校驗(yàn)規(guī)則的結(jié)果分為兩個(gè)部分,放到一個(gè)map中返回乘寒,map的鍵是Boolean類(lèi)型望众,值為元素的列表List。

    @Test
    public void partitioningBy() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 求平均值
        Map<Boolean, List<Integer>> ret = list.stream()
                .collect(Collectors.partitioningBy(
                        new Predicate<Integer>() {
                            @Override
                            public boolean test(Integer integer) {
                                return integer % 2 == 0;
                            }
                        },
                        Collectors.toList()));

        for (Map.Entry<Boolean, List<Integer>> entry : ret.entrySet()) {
            Boolean mapKey = entry.getKey();
            List<Integer> mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }
    }

3.1.2.14 toMap

? ? ? ?toMap方法是根據(jù)給定的鍵生成器和值生成器生成的鍵和值保存到一個(gè)map中返回伞辛,鍵和值的生成都依賴于元素烂翰,可以指定出現(xiàn)重復(fù)鍵時(shí)的處理方案和保存結(jié)果的map。

    @Test
    public void toMap() {
        List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
        Map<Integer, List<Student>> ret = list.stream()
                .collect(Collectors.toMap(
                        // key
                        new Function<Student, Integer>() {
                            @Override
                            public Integer apply(Student student) {
                                return student.getAge();
                            }
                        },
                        // value
                        new Function<Student, List<Student>>() {


                            @Override
                            public List<Student> apply(Student student) {
                                return Lists.newArrayList(student);
                            }
                        },
                        // 有系統(tǒng)key的value怎么處理
                        new BinaryOperator<List<Student>>() {

                            @Override
                            public List<Student> apply(List<Student> students, List<Student> students2) {
                                students.addAll(students2);
                                return students;
                            }
                        }));

        for (Map.Entry<Integer, List<Student>> entry : ret.entrySet()) {
            Integer mapKey = entry.getKey();
            List<Student> mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }

    }


    private static class Student {
        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

3.1.2.15 summarizingInt/summarizingLong/summarizingDouble

? ? ? ?這三個(gè)方法適用于匯總的蚤氏,返回值分別是IntSummaryStatistics甘耿,LongSummaryStatistics,DoubleSummaryStatistics竿滨。

    @Test
    public void summarizingInt() {
        List<Integer> list = Arrays.asList(1, 2, 3);
        IntSummaryStatistics ret = list.stream()
                .collect(Collectors.summarizingInt(
                        new ToIntFunction<Integer>(){
                            @Override
                            public int applyAsInt(Integer value) {
                                return value;
                            }
                        }));
        System.out.println(ret.getAverage());
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棵里,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子姐呐,更是在濱河造成了極大的恐慌,老刑警劉巖典蝌,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曙砂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡骏掀,警方通過(guò)查閱死者的電腦和手機(jī)鸠澈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)截驮,“玉大人笑陈,你說(shuō)我怎么就攤上這事】” “怎么了涵妥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坡锡。 經(jīng)常有香客問(wèn)我蓬网,道長(zhǎng)窒所,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任帆锋,我火速辦了婚禮吵取,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锯厢。我一直安慰自己皮官,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布实辑。 她就那樣靜靜地躺著捺氢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徙菠。 梳的紋絲不亂的頭發(fā)上讯沈,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音婿奔,去河邊找鬼缺狠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛萍摊,可吹牛的內(nèi)容都是我干的挤茄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼冰木,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼穷劈!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起踊沸,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤歇终,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后逼龟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體评凝,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年腺律,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奕短。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匀钧,死狀恐怖翎碑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情之斯,我是刑警寧澤日杈,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響达椰,放射性物質(zhì)發(fā)生泄漏翰蠢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(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,316評(píng)論 2 360
  • 正文 我出身青樓腋妙,卻偏偏與公主長(zhǎng)得像默怨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子骤素,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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