JDK Stream流使用介紹

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

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

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

? ? ? ?

一 Stream流創(chuàng)建

? ? ? ?想使用Stream流,首先咱得先創(chuàng)建一個(gè)Stream流對象。創(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對象。相當(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è)流對象
        Stream<Integer> stream = list.stream();
        // TODO: 對流對象做處理
    }

1.1.2 parallelStream()方法

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

    @Test
    public void collectionParallelStream() {

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

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

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

? ? ? ?數(shù)組也可以作為Stream的數(shù)據(jù)源。我們可以通過Arrays.stream()方法把一個(gè)數(shù)組轉(zhuǎn)化成流對象。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è)流對象
        IntStream stream = Arrays.stream(intArray);
        // TODO: 對流對象做處理
    }

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

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

    @Test
    public void bufferedReaderStream() {

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

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

? ? ? ?Files里面多個(gè)生成Stream對象的方法,都是對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: 對流對象做處理
        } 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: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.3 Files.find()

? ? ? ?Files.find方法用于遍歷查找(過濾)子文件。參數(shù)里面會指定查詢(過濾)條件。把過濾出來的子文件作為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) -> {
               // 過濾掉目錄文件
               return !basicFileAttributes.isDirectory();
           });
            // TODO: 對流對象做處理
        } 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對象
            Stream<String> stream = Files.lines(path);
            // TODO: 對流對象做處理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.5 自己構(gòu)建Stream

? ? ? ?我們也可以自己去創(chuàng)建Stream自己提供數(shù)據(jù)源。Stream類里面提供of()、iterate()、generate()、builder()等一些方法來創(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()可以用來生成無限流,函數(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);
                    }
                });
        // 簡單輸出
        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è)無限流。參數(shù)用于獲取每個(gè)元素。

        // Stream.generate() 生成無限流
        Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
            @Override
            public Double get() {
                return java.lang.Math.random() * 100;
            }
        });
        // 簡單輸出前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()通過建造者模式生成一個(gè)Stream建造器。然后把需要加入Stream里面的數(shù)據(jù)源一個(gè)一個(gè)通過建造器添加進(jìn)去。

        // Stream.builder()構(gòu)造一個(gè)Stream對象
        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: 對流對象做處理

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

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

  • Random.ints()。
  • BitSet.stream()。
  • Pattern.splitAsStream(java.lang.CharSequence)
  • JarFile.stream()。
  • ...

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

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

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

Stream提供的流操作符。

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

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

2.1 filter

? ? ? ?filter用于對流里面的數(shù)據(jù)做過濾操作。

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

2.2 map

? ? ? ?map用于對流里面的元素做轉(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);
            }
        });
        // 簡單輸出
        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)換成對應(yīng)的類型。

    // 轉(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;
            }
        });
        // 簡單輸出總和
        System.out.println(mapStream.sum());
    }

2.4 flatMap、flatMapToInt、flatMapToLong、flatMapToDouble

? ? ? ?flatMap、flatMapToInt、flatMapToLong、flatMapToDouble也是對每個(gè)元素的轉(zhuǎn)換,不過他們和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();
                    }
                });
        // 簡單輸出
        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();
        // 簡單輸出
        rerStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }
    distinct根據(jù)某個(gè)字段去重。

2.6 sorted

? ? ? ?sorted操作符用于對流里面的元素排序。

    // 排序
    @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;
                }
            }
        });
        // 簡單輸出
        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);
            }
        });
        // 簡單輸出
        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);
        // 簡單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

2.9 skip

? ? ? ?skip用于跳過里面的多少個(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);
        // 簡單輸出
        reStream.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }

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

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

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

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

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

3.1 collect()

? ? ? ?collect()的使用主要在于對參數(shù)的理解,所有我們這里要專門講下collect()函數(shù)的參數(shù)Collector這個(gè)類,以及怎么去構(gòu)建Collector對象。只有在了解了這些之后,咱們才可以熟練的把他們用在各種場景中。

3.1.1 Collector

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

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

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

    /**
     * A對應(yīng)supplier()函數(shù)創(chuàng)建的結(jié)果容器
     * T對應(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)用,且該收集器可以并行歸約流。如果收集器沒有標(biāo)為UNORDERED,
         * 那它僅在用于無序數(shù)據(jù)源時(shí)才可以并行歸約
         * 多線程并行
         */
        CONCURRENT,

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

        /**
         * 無需轉(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);



}

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

    // 自己來組裝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é)果容器,容器類型為,我們這里為List<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;
                            }
                        };
                    }

                    // 可以對最終的結(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的工具類,用來創(chuàng)建Collector對象。它內(nèi)部已經(jīng)給我們提供了各種各樣的創(chuàng)建Collector對象的方法,已經(jīng)能滿足我們大部分的需求了,我們可以直接拿來使用,非常方便。

Collectors各個(gè)方法介紹

public final class Collectors {


    /**
     * 將流中的元素全部放置到一個(gè)集合中返回
     *
     * collectionFactory參數(shù)用于創(chuàng)建Collection對象(比如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è)無序集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è)映射是首先對流中的每個(gè)元素進(jìn)行映射,即類型轉(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);

    /**
     * 該方法是在歸納動作結(jié)束之后,對歸納的結(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,首先通過給定的mapper將元素轉(zhuǎn)換類型,然后再求和
     */
    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,首選通過參數(shù)將元素轉(zhuǎn)換為指定的類型
     */
    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ī)約 對流中的元素做統(tǒng)計(jì)歸納作用
     * 和Stream類里面的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類型,值為元素的列表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);
}

? ? ? ?為了加深大家的理解。接下來我們對Collectors里面的每個(gè)函數(shù)都寫一個(gè)簡單的實(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è)無序集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è)映射是首先對流中的每個(gè)元素進(jìn)行映射,即類型轉(zhuǎn)換,然后再將新元素以給定的Collector進(jìn)行歸納。

    @Test
    public void mapping() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // 先把流里面的每個(gè)元素的前后加上[],之后在用:拼接起來
        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

? ? ? ?該方法是在歸納動作結(jié)束之后,對歸納的結(jié)果進(jìn)行再處理。

    @Test
    public void collectingAndThen() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // Collectors.toList()之后在把List<String>通過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è)在編碼過程經(jīng)常用到的場景,根據(jù)list中對象的屬性去重。
    @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");
        // 這里簡單的用字符串比較的大小
        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,首先通過給定的mapper將元素轉(zhuǎn)換類型,然后再求和。

    @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,首選通過參數(shù)將元素轉(zhuǎn)換為指定的類型。

    @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方法對應(yīng)的,二者是可以替換使用的,作用完全一致,也是對流中的元素做統(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ù)最長的函數(shù)每個(gè)參數(shù)的含義。

  • Function<? super T, ? extends K> classifier:確定怎么從流里面每個(gè)元素獲取key。
  • Supplier<M> mapFactory:用于創(chuàng)建最終要生成的對象一般都是Map。
  • Collector<? super T, A, D> downstream:每一組數(shù)據(jù)用什么來存放,一般是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是一樣的,只不過groupingByConcurrent生成的是ConcurrentMap,而groupingBy生成的是Map。

3.1.2.13 partitioningBy

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

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