java8 Stream使用总结

【前言】 java8新特性

java8 函数接口

java8 Optional使用总结

Java 8 时间日期使用

java8 lambda表达式

 

1、流的介绍

Java8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。
而在Java 的集合 API 中,仅仅有极少量的辅助型方法,更多的时候是程序员需要用 Iterator 来遍历集合,完成相关的聚合应用逻辑。
所谓聚合操作就类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

2、流的特点

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator,用户只要给出需要对其包含的元素执行什么操作,Stream 会隐式地在内部进行遍历,做出相应的数据转换,比如 “过滤掉长度大于 10 的字符串”。而原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作。
Stream 如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了。和迭代器不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作,Stream 的并行操作依赖于 Java7 中的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

3、流的操作类型

中间操作(intermediate )
一个流可以后面跟随零个或多个 intermediate 操作。其目的是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),仅仅调用到这类方法,并没有真正开始流的遍历。
对应方法有map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered等

终止操作(terminal )
一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用尽了,无法再被操作,这是流的最后一个操作。
Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果或者一个错误。
对应方法有forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator

短路操作(short-circuiting)
对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
对应方法有anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

4、流的生成

流的来源可以是集合,数组,I/O channel, 产生器generator 等。
基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。

示例如下:

        /*************流的来源*************/
        // 1、of方法
        //      of(T... values):返回含有多个T元素的Stream
        //      of(T t):返回含有一个T元素的Stream
        Stream<String> single = Stream.of("a");
        Stream<String> multiple = Stream.of("a", "b", "c");

        // 2、generator方法,返回一个无限长度的Stream,其元素由Supplier接口的提供。
        Stream<String> generate = Stream.generate(() -> "a");
        Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
            @Override
            public Double get() {
                return java.lang.Math.random();
            }
        });
        //  lambda写法
        Stream<Double> generateB = Stream.generate(()-> java.lang.Math.random());
        Stream<Double> generateC = Stream.generate(java.lang.Math::random);

        // 3、iterate方法,返回的也是一个无限长度的Stream,与generate方法不同的是,其是通过函数f迭代对给指定的元素种子而产生无限连续有序Stream,其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
        Stream<Integer> iterate = Stream.iterate(1, item -> item + 2);
        // 无限流处理
        iterate.limit(10).forEach(System.out::println);

        // 4、empty方法返回一个空的顺序Stream,该Stream里面不包含元素项。
        Stream<Object> empty = Stream.empty();

        // 5、Collection接口和数组的默认方法
        String chars[] = new String[]{"a", "b", "c"};
        Arrays.stream(chars).forEach(System.out::println);

        List list = Arrays.asList(chars);
        list.stream().forEach(System.out::println);

5、流的相关使用

当你熟悉使用Lambda表达式时,就可以很方便的进行流相关的操作了,提高代码的编写效率和程序的可读性。

     /*************流的操作*************/
        //concat 将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都时排序的,则新Stream也是排序的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的;若关闭新的Stream时,原两个输入的Stream都将执行关闭处理。
        Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5))
                .forEach(integer -> System.out.print(integer + "  "));

        //distinct 去除掉原Stream中重复的元素,生成的新Stream中没有没有重复的元素。
        Stream.of(1,1,3,4,3).distinct().forEach(System.out::println);

        //filter 对原Stream按照指定条件过滤,过滤出满足条件的元素。
        Stream.of(1,1,3,4,3).filter(x -> x > 2).forEach(System.out::println);

        //map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。
        //为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong,将原Stream中的数据类型,转换为double,int或者long类型。
        Stream.of("a", "b", "c")
                .map(item -> item.toUpperCase()) // .map(String::toUpperCase)
                .forEach(System.out::println);

        // flatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数转换,
        // 不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。
        List<User> users = UserUtil.getUsers(6);
        users.stream()
                .flatMap(s -> s.getInterests().stream())
                .forEach(System.out::println);

        Stream.of("a", "b", "c").flatMap(s -> Stream.of(s.toUpperCase()));

        //peek 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例)
        Stream.of("a", "b", "c")
                //优先执行
                .peek(s -> System.out.println("peek:" + s))
                .forEach(System.out::println);

        //skip 将过滤掉原Stream中的前N个元素,返回剩下的元素所组成的新Stream。
        // 如果原Stream的元素个数大于N,将返回原Stream的后的元素所组成的新Stream;
        // 如果原Stream的元素个数小于或等于N,将返回一个空Stream。
        Stream.of("a", "b", "c").skip(2)
                .forEach(System.out::println);

        // sorted方法将对原Stream进行排序,返回一个有序列的新Stream。sorterd有两种变体sorted(),sorted(Comparator),
        // 前者将默认使用Object.equals(Object)进行排序,而后者接受一个自定义排序规则函数(Comparator),可自定义进行排序。
        Stream.of(5, 6, 3, 9, 1)
                .sorted()
                .forEach(System.out::println);

        System.out.println("+++++++++++++++++++++++++++++++");
        Stream.of(5, 6, 3, 9, 1)
                .sorted(new Comparator<Integer>() {
                    @Override
                    public int compare(Integer o1, Integer o2) {
                        //asc
                        return o1 - o2;
                    }
                })
                .forEach(System.out::println);

        Stream.of(5, 6, 3, 9, 1)
                //desc
                .sorted(((o1, o2) -> o2 - o1))
                .forEach(System.out::println);
        System.out.println("+++++++++++++++++++++++++++++++");


        // count 将返回Stream中元素的个数。
        long count = Stream.of(1, 2, 3, 4, 5).count();
        System.out.println("count:" + count);

        // forEach 用于遍历Stream中的所元素,避免了使用for循环,让代码更简洁,逻辑更清晰。
        Stream.of("a", "b", "c").forEach(System.out::println);

        // forEachOrdered 与forEach类似,都是遍历Stream中的所有元素,
        // 不同的是,如果该Stream预先设定了顺序,会按照预先设定的顺序执行(Stream是无序的),默认为元素插入的顺序。
        Stream.of(5,2,1,4,3)
                .forEachOrdered(integer ->
                    System.out.println("integer:" + integer)
                );

        // max 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最大的元素。
        Optional<Integer> max = Stream.of(5, 2, 2, 3, 4, 8)
                .max((o1, o2) -> o2 - o1);
//        Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5)
//                .max(Comparator.comparingInt(x -> x));
        Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5)
                .max((o1, o2) -> o1 - o2);
        int max2 = Stream.of(1, 2, 3, 4, 5)
                .mapToInt(x -> x).max().getAsInt();
        System.out.println("max = " + max.get() + "  max2 = " + max2 + "  max3 = " + max3.orElse(-1));

        UserUtil.getUsers(6).stream()
                .sorted(Comparator.comparing(User::getName).thenComparing(User::getId))
                .forEach(u -> System.out.println(u.getName()));

        // min 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最小的元素。
        Optional<Integer> min = Stream.of(1, 2, 3, 4, 5)
                .min((o1, o2) -> o1 - o2);
        System.out.println("min:" + min.get());

        System.out.println("*********************reduce********************");
        // reduce
        //  1、reduce((sum, item) -> { ... }); //返回Optional,因为可能存在为空的情况,
        //  2、reduce(0, (sum, item) -> { ... }); /返回对应类型,不存在为空的情况
        //无初始值,第一个参数为stream的第一个元素,第二个参数为stream的第二个元素,计算的结果赋值给下一轮计算的sum
        Optional<Integer> optional = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> {
            System.out.println("sum before:" + sum);
            System.out.println("item:" + item);
            sum = sum + item;
            System.out.println("sum after:" + sum);

            return sum;

//            return Integer.sum(sum, item);
        });
        //等效
        Optional<Integer> optional1 = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) ->
                Integer.sum(sum, item)
        );
        //等效
        Optional<Integer> optional2 = Stream.of(1, 2, 3, 4, 5).reduce(Integer::sum);
        System.out.println("integer = " + optional.orElse(-1));
        System.out.println("*****************************************");
        //给定初始值,第一个参数为初始值,第二个参数为stream的第一个元素,计算的结果赋值给下一轮计算的sum
        Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(5, (sum, item) -> {
            System.out.println("sum2 before:" + sum);
            System.out.println("item:" + item);
            sum = sum + item;
            System.out.println("sum2 after:" + sum);
            return sum;
        });
        //等效
        Integer reduce2 = Stream.of(1, 2, 3, 4, 5).reduce(0, (sum, item) ->
                Integer.sum(sum, item)
        );
        //等效
        Integer reduce3 = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
        System.out.println("reduce = " + reduce);


        System.out.println("*********************collect********************");
        List<Integer> toList = Stream.of(1, 2, 3, 4)
                .collect(Collectors.toList());
        List<Integer> toList2 = Stream.of(1, 2, 3, 4)
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println("toList: " + toList);

        Set<Integer> toSet = Stream.of(1, 2, 3, 4)
                .collect(Collectors.toSet());
        Set<Integer> toSet2 = Stream.of(1, 2, 3, 4)
                .collect(Collectors.toCollection(() -> new TreeSet()));
        System.out.println("toSet: " + toSet);

        //(value1, value2) -> value1  用前面的value覆盖后面的value,保持不变
        List<User> userList = UserUtil.getUsers(5);
        userList.add(new User(2, "fjw"));
        Map<Integer, String> toMap = userList.stream()
                .collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value1));
        System.out.println("(value1, value2) -> value1");
        toMap.forEach((k, v) -> System.out.println(k + "-" + v));

        // 对value值进行了限定不能为null,否则抛出空指针异常
//        userList.add(new User(3, null));
        //(value1, value2) -> value2  用后面的value覆盖前面的value
        Map<Integer, String> toMap2 = userList.stream()
                .collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value2));
        System.out.println("(value1, value2) -> value2");
        toMap2.forEach((k, v) -> System.out.println(k + "-" + v));

        // 解决value值为null方式
        userList.add(new User(4, null));
        Map<Integer, String> toMap3 = userList.stream()
                .collect(HashMap::new, (m, u) -> m.put(u.getId(), u.getName()), HashMap::putAll);
        toMap3.forEach((k, v) -> System.out.println(k + "-" + v));

        Optional<Integer> maxBy = Stream.of(1, 2, 3, 4)
                .collect(Collectors.maxBy(Comparator.comparingInt(o -> o)));
        System.out.println("maxBy:" + maxBy.get());

        Long counting = Stream.of(1, 2, 3, 4)
                .collect(Collectors.counting());
        System.out.println("counting:" +counting);

        //分割数据块
        Map<Boolean, List<Integer>> partitioningBy = Stream.of(1, 2, 3, 4, 5)
                .collect(Collectors.partitioningBy(item -> item > 3));
        //partitioningBy : {false=[1, 2, 3], true=[4, 5]}
        System.out.println("partitioningBy : " + partitioningBy);

        Map<Boolean, Long> collect = Stream.of(1, 2, 3, 4)
                .collect(Collectors.partitioningBy(item -> item > 3, Collectors.counting()));
        System.out.println("collect: " + collect);

        //数据分组
        Map<Boolean, List<Integer>> groupingBy = Stream.of(1, 2, 3, 4, 5)
                .collect(Collectors.groupingBy(item -> item > 3));
        //partitioningBy : {false=[1, 2, 3], true=[4, 5]}
        System.out.println("groupingBy : " + groupingBy);

        //字符串
        String joining = Stream.of("a", "b", "c", "d")
                .collect(Collectors.joining(","));
        System.out.println(joining);

        String joining2 = Stream.of("a", "b", "c", "d")
                .collect(Collectors.joining(",", "[", "]"));
        System.out.println(joining2);

      // 根据id分组
      Map<Integer, List<User>> collect3 = userList.stream().collect(Collectors.groupingBy(User::getId));
      // 将id映射为id集合
      List<Integer> collect1 = userList.stream().collect(Collectors.mapping(User::getId, Collectors.toList()));
      // 根据id分组后将interests映射成集合
      Map<Integer, List<List<String>>> collect2 = userList.stream()
      .collect(Collectors.groupingBy(User::getId, Collectors.mapping(User::getInterests, Collectors.toList())));
      System.out.println(collect3);
      System.out.println(collect1);
      System.out.println(collect2);

 

源码参照Github

posted @ 2019-07-10 09:40  花拾夕  阅读(4014)  评论(0编辑  收藏  举报