• 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

  • 相关阅读:
    【BZOJ3261】— 最大异或和(可持久化0/1Trie)
    【ZJOI2007】—捉迷藏(动态点分治)
    【HNOI2010】-城市建设(动态最小生成树)
    【BOI2007】Mokia 摩基亚
    【BZOJ 3262】-陌上花开(CDQ分治+树状数组)
    Win64 驱动内核编程-24.64位驱动里内嵌汇编
    Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook
    Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook
    Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)
    Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)
  • 原文地址:https://www.cnblogs.com/kingsonfu/p/11161888.html
Copyright © 2020-2023  润新知