美文网首页
JDK Stream流使用介绍

JDK Stream流使用介绍

作者: tuacy | 来源:发表于2019-07-20 08:51 被阅读0次

       Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。通常我们需要多行代码才能完成的操作,借助于Stream流式处理可以很简单的实现。

       Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。同时Stream提供串行和并行两种模式进行汇聚操作。比如你的Stream里面有很多数据,Stream可以开多个线程每个线程处理一部分。最后把结果汇总起来。

       在开始之前我们先用一个图来整体的概况下Stream。如下所示:

       

一 Stream流创建

       想使用Stream流,首先咱得先创建一个Stream流对象。创建Steam需要数据源.这些数据源可以是集合、可以是数组、可以使文件、甚至是你可以去自定义等等。

1.1 集合作为Stream数据源

       集合Collection作为Stream的数据源,应该也是我们用的最多的一种数据源了。Collection里面也提供了一些方法帮助我们把集合Collection转换成Stream。

1.1.1 stream()方法

       调用Collection.stream()函数创建一个Stream对象。相当于把集合Collection里面的数据都导入到了Stream里面去了。

    @Test
    public void collectionStream() {

        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        // 使用List创建一个流对象
        Stream<Integer> stream = list.stream();
        // TODO: 对流对象做处理
    }

1.1.2 parallelStream()方法

       调用Collection.parallelStream()创建Stream对象。

    @Test
    public void collectionParallelStream() {

        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        // 使用List创建一个流对象
        Stream<Integer> stream = list.parallelStream();
        // TODO: 对流对象做处理
    }

parallelStream()与stream()区别是parallelStream()使用多线程并发处理最后子啊汇总结果,而stream()是单线程。所以相对来说parallelStream()效率要稍微高点。

1.2 数组作为Stream数据源

       数组也可以作为Stream的数据源。我们可以通过Arrays.stream()方法把一个数组转化成流对象。Arrays.stream()方法很丰富,有很多个。大家可以根据实际情况使用。

    @Test
    public void arrayStream() {

        int[] intArray = new int[10];
        for (int index = 0; index < intArray.length; index++) {
            intArray[index] = index;
        }
        // 使用数组创建一个流对象
        IntStream stream = Arrays.stream(intArray);
        // TODO: 对流对象做处理
    }

1.3 BufferedReader作为Stream数据源

       我们也可以把BufferedReader里面lines方法把BufferedReader里面每一行的数据作为数据源生成一个Stream对象。

    @Test
    public void bufferedReaderStream() {

        File file = new File("/home/tuacy/github/google-guava-study/src/main/resources/application.yml");
        try {
            // 把文件里面的内容一行一行的读出来
            BufferedReader in = new BufferedReader(new FileReader(file));
            // 生成一个Stream对象
            Stream<String> stream = in.lines();
            // TODO: 对流对象做处理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4 File作为Stream数据源

       Files里面多个生成Stream对象的方法,都是对Path(文件)的操作。有的是指定Path目录下所有的子文件(所有的子文件相当于是一个列表了)作为Stream数据源,有的把指定Path文件里面的每一行数据作为Stream的数据源。

1.4.1 Files.list()

       列出指定Path下面的所有文件。把这些文件作为Stream数据源。

    @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()方法用于遍历子文件(包括文件夹)。参数maxDepth用于指定遍历的深度。把子文件(子文件夹)作为Stream数据源。

    @Test
    public void fileWalkStream() {
        Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
        try {
            // 第二个参数用于指定遍历几层
            Stream<Path> stream = Files.walk(path, 2);
            // TODO: 对流对象做处理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4.3 Files.find()

       Files.find方法用于遍历查找(过滤)子文件。参数里面会指定查询(过滤)条件。把过滤出来的子文件作为Stream的数据源。

    @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文件里面的每一行内容作为Stream的数据源。

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

1.5 自己构建Stream

       我们也可以自己去创建Stream自己提供数据源。Stream类里面提供of()、iterate()、generate()、builder()等一些方法来创建Stream,Stream的数据源我们自己提供。

1.5.1 Stream.of()

       Stream.of()函数参数就是数据源。

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

1.5.2 Stream.iterate()

       Stream.iterate()可以用来生成无限流,函数需要两个参数:第一个参数是初始值、第二个参数用于确定怎么根据前一个元素的值生成下一个元素。

        // Stream.iterate() 流式迭代器 Stream.iterate()函数的第二个参数告诉你怎么去生成下一个元素
        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()也是用于生成一个无限流。参数用于获取每个元素。

        // Stream.generate() 生成无限流
        Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
            @Override
            public Double get() {
                return java.lang.Math.random() * 100;
            }
        });
        // 简单输出前10个值
        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()通过建造者模式生成一个Stream建造器。然后把需要加入Stream里面的数据源一个一个通过建造器添加进去。

        // Stream.builder()构造一个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创建方式

       Stream其他创建方式我们就不一一举例了。有如下方式。

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

二 Stream流操作(操作符)

       Stream流操作就是对Stream流的各种处理。Stream里面已经给提供了很多中间操作(我们一般称之为操作符)。

Stream提供的流操作符。

Stream流操作符 解释
filter 过滤
map 对流里面每个元素做转换
mapToInt 把流里面的每个元素转换成int
mapToLong 流里面每个元素转换成long
mapToDouble 流里面每个元素转换成double
flatMap 流里面每个元素转换成Steam对象,最后平铺成一个Stream对象
flatMapToInt 流里面每个元素转换成IntStream对象,最后平铺成一个IntStream对象
flatMapToLong 流里面每个元素转换成LongStream对象,最后平铺成一个LongStream对象
flatMapToDouble 流里面每个元素转换成DoubleStream对象,最后平铺成一个DoubleStream对象
distinct 去重
sorted 排序
peek 查看流里面的每个元素
limit 返回前n个数
skip 跳过前n个元素

       Stream提供了这么多的操作符,而且这些操作符是可以组合起来使用。关于每个操作符的使用我们用一个简单的实例代码来说明。

2.1 filter

       filter用于对流里面的数据做过滤操作。

    // 过滤
    @Test
    public void filter() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 过滤出偶数
        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用于对流里面的元素做转换。

    // 转换
    @Test
    public void map() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 整数转换为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用于将流里面的元素转换成对应的类型。

    // 转换
    @Test
    public void mapToInt() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 整数转换为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也是对每个元素的转换,不过他们和map的不同点在于,他们是吧每个元素转换成一个Stream流,最终在平铺成一个Stream流。

    // 转换
    @Test
    public void flatMap() {
        Stream<String> stream = Stream.of("java:1", "android:2", "ios:3");
        // 整数转换为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);
            }
        });
    }
    // 转换
    @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);
            }
        });
    }

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操作符用于查看流里面的每个元素。在多个操作符同时使用的时候的中间加入peek操作符可以参考每个操作符之后的结果。

    // 查看
    @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操作符用于取流前面多少个元素。

    // 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用于跳过里面的多少个元素。

    // 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流终端操作是流式处理的最后一步,之前已经对Stream做了一系列的处理之后。该拿出结果了。我们可以在终端操作中实现对流的遍历、查找、归约、收集等等一系列的操作。

Stream流终端操作提供的函数有

终端操作符 解释
forEach 遍历
forEachOrdered 如果流里面的元素是有顺序的则按顺序遍历
toArray 转换成数组
reduce 归约 - 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
collect 收集 - 对处理结果的封装
min 最小值
max 最大值
count 元素的个数
anyMatch 任何一个匹配到了就返回true
allMatch 所有都匹配上了就返回true
noneMatch 没有一个匹配上就返回true
findFirst 返回满足条件的第一个元素
findAny 返回某个元素

       关于Stream终端操作部分,我们就着重讲下collect()函数的使用。因为其他的终端操作符都很好理解。collect()稍稍复杂一点。

3.1 collect()

       collect()的使用主要在于对参数的理解,所有我们这里要专门讲下collect()函数的参数Collector这个类,以及怎么去构建Collector对象。只有在了解了这些之后,咱们才可以熟练的把他们用在各种场景中。

3.1.1 Collector

       Collector类目前没别的用处,就是专门用来作为Stream的collect()方法的参数的。把Stream里面的数据转换成我们最终想要的结果上。

Collector各个方法,以及每个泛型的介绍,都在下面详细的解释了。如果有疑问可以留言。

/**
 * Collector是专门用来作为Stream的collect方法的参数的
 *
 * 泛型含义
 * T:是流中要收集的对象的泛型
 * A:是累加器的类型,累加器是在收集过程中用于累积部分结果的对象。
 * R:是收集操作得到的对象(通常但不一定是集合)的类型。
 */
public interface Collector<T, A, R> {
    /**
     * 生成结果容器,容器类型为A
     * (多线程的情况下可能会调用多次,开多个线程同时去处理一个流,每个线程调用一次)
     */
    Supplier<A> supplier();

    /**
     * A对应supplier()函数创建的结果容器
     * T对应Stream流里面一个一个的元素
     * 用于消费元素,也就是归纳元素,一般在这个里面把流里面的元素T(也可以转换下)放到supplier()创建的结果T里面去
     */
    BiConsumer<A, T> accumulator();

    /**
     * 用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
     * 多线程的情况下,多个线程并行执行。每个线程产生一个结果
     */
    BinaryOperator<A> combiner();

    /**
     * 用于将之前整合完的结果A转换成为R
     *
     * combiner()完成之后了A, 这里还可以在转一道。生成你自己想要的结果
     */
    Function<A, R> finisher();

    /**
     * characteristics表示当前Collector的特征值,
     * 这是个不可变Set
     * 它定义了收集器的行为--尤其是关于流是否可以多线程并行执行,以及可以使用哪些优化的提示
     */
    Set<Characteristics> characteristics();

    /**
     * 它定义了收集器的行为--尤其是关于流是否可以并行归约,以及可以使用哪些优化的提示
     */
    enum Characteristics {
        /**
         * accumulator函数可以从多个线程同时调用,且该收集器可以并行归约流。如果收集器没有标为UNORDERED,
         * 那它仅在用于无序数据源时才可以并行归约
         * 多线程并行
         */
        CONCURRENT,

        /**
         * 归约结果不受流中项目的遍历和累积顺序的影响(无序)
         */
        UNORDERED,

        /**
         * 无需转换结果
         */
        IDENTITY_FINISH
    }


    /**
     * 四参方法,用于生成一个Collector,T代表流中的一个一个元素,R代表最终的结果
     */
    public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
                                              BiConsumer<R, T> accumulator,
                                              BinaryOperator<R> combiner,
                                              Characteristics... characteristics);

    /**
     * 五参方法,用于生成一个Collector,T代表流中的一个一个元素,A代表中间结果,R代表最终结果,finisher用于将A转换为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一个Collector对象,把我们Steam流里面的数据转换成List。(当然了Collectors类里面有提供这个方法,这里我们自己写一个也是为了方便大家的理解)

    // 自己来组装Collector,返回一个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>>() {
                    // 生成结果容器,容器类型为,我们这里为List<Integer>
                    @Override
                    public Supplier<List<Integer>> supplier() {
                        return new Supplier<List<Integer>>() {

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

                    // 把流里面的结果都放到结果容器里面去
                    @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);
                            }
                        };
                    }

                    // 两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果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;
                            }
                        };
                    }

                    // 可以对最终的结果做一个转换操作
                    @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的工具类,用来创建Collector对象。它内部以及给我们提供了各种各样的创建Collector对象的方法,已经能满足我们大部分的需求了,我们可以直接拿来使用,非常方便。

Collectors各个方法介绍

public final class Collectors {


    /**
     * 将流中的元素全部放置到一个集合中返回
     *
     * collectionFactory参数用于创建Collection对象(比如List,LinkeList等等)
     */
    public static <T, C extends Collection<T>>
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);

    /**
     * 将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList
     */
    public static <T>
    Collector<T, ?, List<T>> toList();

    /**
     * 将流中的元素放置到一个无序集set中去。默认为HashSet
     */
    public static <T>
    Collector<T, ?, Set<T>> toSet();

    /**
     * joining的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀
     */
    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);

    /**
     * 这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳
     */
    public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream);

    /**
     * 该方法是在归纳动作结束之后,对归纳的结果进行再处理
     */
    public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                                Function<R,RR> finisher);

    /**
     * 该方法用于计数
     */
    public static <T> Collector<T, ?, Long> counting();

    /**
     * 生成一个用于获取最小/最大值的Optional结果的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);

    /**
     * 生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和
     */
    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);

    /**
     * 生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型
     */
    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);

    /**
     * 规约 对流中的元素做统计归纳作用
     * 和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);

    /**
     * 这个方法是用于生成一个拥有分组功能的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);

    /**
     * 该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个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);

    /**
     * 根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,
     * 可以指定出现重复键时的处理方案和保存结果的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);

    /**
     * 并发版本的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);

    /**
     * 这三个方法适用于汇总的,返回值分别是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里面的每个函数都写一个简单的实例。

3.1.2.1 toCollection

       将流中的元素全部放置到一个集合中返回,这里使用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

       将流中的元素放置到一个列表集合中去。这个列表默认为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

       将流中的元素放置到一个无序集set中去。默认为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的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀。

    @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

       这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。

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

       该方法是在归纳动作结束之后,对归纳的结果进行再处理。

    @Test
    public void collectingAndThen() {
        List<String> list = Arrays.asList("java", "ios", "c");
        // Collectors.toList()之后在把List<String>通过Joiner转换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
    }

3.1.2.7 counting

       计算流里面的元素个数。

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

3.1.2.8 minBy/maxBy

       生成一个用于获取最小/最大值的Optional结果的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

       生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和。

    @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

       生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型。

    @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方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用(规约)。

    @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

       这个方法是用于生成一个拥有分组功能的Collector,它也有三个重载方法。这里要稍微解释下参数最长的函数每个参数的含义。

  • Function<? super T, ? extends K> classifier:确定怎么从流里面每个元素获取key。
  • Supplier<M> mapFactory:用于创建最终要生成的对象一般都是Map。
  • Collector<? super T, A, D> downstream:每一组数据用什么来存放,一般是List,所以这个参数一般是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

       该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个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方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的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);
                            }
                        },
                        // 有系统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

       这三个方法适用于汇总的,返回值分别是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());
    }

       关于JDK8里面Stream部分内容我们就讲这么些,真心希望能帮助上大家。大家如果有什么疑问可以留言。最后,上面所有的实例代码在 https://github.com/tuacy/google-guava-study里面都可以找到。

相关文章

  • JDK Stream流使用介绍

    Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合...

  • Stream流的使用

    Stream流的使用 本篇主要讲解JDK8中 Stream流的使用, 包括如何 筛选 、切片、映射 、查找、匹配 ...

  • Java8 Stream流中的 collect() 方法,远比你

    Stream流 collect() 方法的使用介绍 Stream 流的注意事项:Stream不调用终止方法,中间的...

  • JavaStream流基础学习

    Stream流 Straem流使用 使用Sream流: 一行搞定 1.2 Stream流生成方式 Stream流的...

  • Java | Stream

    简介 Stream流 是 JDK1.8 加入的新特性,Stream流跟I/O流不同,Stream流用于操作数据源(...

  • jdk stream流

  • Guli的技术点

    jdk8的新特性有那些: 项目使用到的stream流操作 常用方法以及示例:https://www.jianshu...

  • JDK8stream将list转Map对象报错java.lang

    ​ JDK8有很多新特性,比如lambda表达式,函数式编程以及stream流的使用,这几个新特性,使用过之后就爱...

  • jdk8 Stream流

    jdk8 Stream流 1.Stream API 简介 Stream API是Java 8中加入的一套新的API...

  • jdk新特性 --> Stream流

    jdk 并行流 求和 java stream 流动 多条件分组和规约 1. 概述 Stream 是 Java8 中...

网友评论

      本文标题:JDK Stream流使用介绍

      本文链接:https://www.haomeiwen.com/subject/pmlylctx.html