十五、Stream 流操作

2020-07-19  本文已影响0人  blank_white
流的简单使用
        Integer[] nums = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(nums);

        // 过滤掉 不是 > 2 的,并统计元素数量
        long count = list.stream().filter(n -> n > 2).count();
        System.out.println(count);
        // 上面过程的并行版
        count = list.parallelStream().filter(n -> n > 2).count();
        System.out.println(count);

流的获取
        Integer[] nums = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(nums);


        // 1、将 Collection 转为 Stream
        // Collection 接口下的 stream 和 parallelStream 方法 可以返回一个 Stream
        Stream<Integer> stream = list.stream();
        // 并行流,后续操作会以并行方式运行
        Stream<Integer> stream1 = list.parallelStream();


        // 2、Steam.of 方法直接传入对象
        // public static<T> Stream<T> of(T... values)
        // 这里如果传入 基本类型的数组的话, int[] ,泛型对不上,nums 会作为 int[] 类型的一个参数被传递
        Stream<Integer> stream2 = Stream.of(nums);
        stream2.forEach(System.out::println); // [I@11096447
        stream2 = Stream.of(1, 2, 3, 6);

        // 产生一个不包含任何元素的流
        Stream.empty();

        // 产生一个无限长度的流,每个结果由参数中的函数过程生成的返回值
        stream2 = Stream.generate(() -> 1);
        // 如产生一个随机数流
        Random random = new Random();
        stream2 = Stream.generate(random::nextInt);
        stream2 = Stream.generate(() -> random.nextInt(100));

        // 产生一个无限长度的流,设置一个初始结果,每次生成的结果会作为下一次生产过程的参数
        // 如产生一个自增流
        stream2 = Stream.iterate(0, n -> ++n);
        Stream<BigInteger> stream22 = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));


        // 3、将数组转换为 Stream
        // Arrays.stream 方法
        // 产生一个 从起始下标,到结束下标(不包括结束下标)的流
        Stream<Integer> stream3 = Arrays.stream(nums, 0, 4);


        // 还有其他很多 类都自带产生流的 方法 如 Pattern.splitAsStream 、 Files.lines
流的转换

将流做一些处理并返回一个流

        Integer[] nums = {1, 2, 3, 4, 5};
        Stream<Integer> stream = Arrays.stream(nums);


        // 流操作一次后不能再次操作,这里不用 stream 重新接收新的流的话,后面再次操作会报错错
        // stream.filter(n -> n > 2);

        // 过滤,将返回 true 的结果作为新的流返回
        stream=stream.filter(n -> n > 2);

        // 对每个元素进行操作,将传入的函数应用到每个元素上,函数的返回值作为每个元素的新值
        stream=stream.map(n->n*10);


        Stream<String> stringStream=Stream.of("what","is","your","name");
        // flatMap 将流里的每个元素都转换为流,再将这些流 合成为一个流返回
        stringStream.flatMap(w->{
            char[] chars = w.toCharArray();
            List<Character> list=new ArrayList<>();
            for(Character c:chars)list.add(c);
            return list.stream();
        }).forEach(System.out::print);
抽取子流和连接流
        Integer[] nums = {1, 2, 3, 4, 5 ,6 ,7, 8};
        Stream<Integer> stream = Arrays.stream(nums);


        // 截取流的 前 n 个元素,作为新的流返回
        stream=stream.limit(4);  // 1 2 3 4

        // 丢弃前 n 个元素,将剩余的元素作为新流返回
        stream=stream.skip(2);  // 3 4      1234跳过前两个剩下34

        // 生成一个 有 100 个随机数的流
        Stream.generate(Math::random).limit(1000);


        Stream<Integer> stream2 = Arrays.stream(nums);
        Stream<Integer> stream3 = Arrays.stream(nums);

        // 拼接两个流 ,第一个流不应该是无限的,否则第二个流始终用不到,没有意义
        stream=Stream.concat(stream2,stream3);  // 1.2.3···8.1.2.3···8


        // 去重,返回一个去掉重复元素的流
        stream.distinct();

        stream.forEach(System.out::println);
流的转换 2
        // 流排序
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 调用 sorted 方法获得一个经过排序的流
        stream=stream.sorted();

        // 传入 比较器  ,传入了一个逆序的比较器,做逆序
        stream=stream.sorted(Comparator.comparing(n->(Integer)n).reversed());



        // 返回一个流,并且当这个流取出元素的时候,会先执行这里的函数过程
        stream=stream.peek(e-> System.out.print("本次使用的元素是"+e+"---"));

        // 这里 调用 forEach 会取出元素,每次取出元素会先输出 本次使用的元素是X 在执行这里的过程
        // 如果没有调用forEach ,也就是没有取出元素,你会发现 peek 方法中的函数过程并不会执行
        stream.forEach(System.out::print); // 本次使用的元素是9---9本次使用的元素是8---8本次使用的元素是7---7 ···

简单约简 ,终结流的操作,将流转为非流值
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 获取流元素数
        long counts=stream.count();


        stream = Arrays.stream(nums);
        // 获取流元素中最大的 (min方法与之用法相同)
        Optional<Integer> max = stream.max(Integer::compare);
        // 获得实际值,参数是当获取不到的时候的默认值
        Integer iMax = max.orElse(0);



        stream = Arrays.stream(nums).filter(n->n>6);
        // 返回第一个匹配的元素
        Optional<Integer> first = stream.findFirst();


        stream = Arrays.stream(nums).filter(n->n>6);
        // 返回任何一个匹配元素,在并行处理流的时候比较有效
        Optional<Integer> any = stream.findAny();



        boolean b;
        // 是否全部匹配
        b=Arrays.stream(nums).allMatch(n->n>1); // false
        // 是否存在匹配
        b=Arrays.stream(nums).anyMatch(n->n>2); // true
        // 是否没有匹配
        b=Arrays.stream(nums).noneMatch(n->n>10);// true
        System.out.println(b);

Optional 类型
        Long[] nums = {6L,3L,1L,4L,5L,3L,9L,7L,6L,4L,2L,1L,8L};
        Stream<Long> stream = Arrays.stream(nums);

        // stream 的一些方法会返回一个包含结果的 Optional 对象
        Optional<Long> first = stream.filter(n -> n > 6L).findFirst();

        Long l;
        // 获得Optional中的值 ,如果没有则返回 传入的 替代物
        l=first.orElse(0L);

        // 当没有值的时候 还可以调用代码获取替代值,(只有在需要替代值的时候才会调用代码)
        l=first.orElseGet(()-> new Date().getTime());

        // 当没有值的时候 抛出异常
        //l= first.orElseThrow(Exception::new);

        // 只有当值存在的时候 调用
        first.ifPresent(System.out::println);

        // 将结果值处理并包装返回
        // 如果 first 有值,返回包装处理结果的 Optional ,
        // 如果 first 没有值,返回空的 Optional(是没有值的一个 Optional 对象,不是返回 null)
        Optional<Integer> aBoolean = first.map((n) -> n==9?1:2);

        // 返回 Optional 中的值,没有值的时候会抛异常,不推荐用
        first.get();
        // 返回一个 boolean 表示是否有值
        first.isPresent();


        /// 创建 Optional 的方法
        // 产生一个 Optional 对象,如果 传入的是 null 会抛异常
        Optional<Integer> integer = Optional.of(2);

        // 产生一个 Optional 对象,如果传入的是 null 会返回一个没有值的 Optional 对象
        Optional<Object> o = Optional.ofNullable(null);

        // 产生一个没有值的 Optional 对象
        Optional<Object> empty = Optional.empty();

        // 将值 处理,并包装成 Optional 并返回
        Optional<String> s = first.flatMap(m -> Optional.ofNullable(m.toString()));
收集结果-流转集合
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 获取 Iterator 来遍历流中的元素
        Iterator<Integer> iterator = stream.iterator();

        // 遍历每个元素并应用函数
        // 在并行流上是没有顺序的
        Arrays.stream(nums).parallel().forEach(System.out::print); // 9674421358316   9674331126458  9673216445138

        // 会按顺序遍历,但是会导致并行处理的性能优势基本丧失
        Arrays.stream(nums).parallel().forEachOrdered(System.out::print);

        // 获得由流元素组成的数组
        Object[] objects = Arrays.stream(nums).toArray();
        // 传入数组构造器,来获得正确的数组类型
        Integer[] integers = Arrays.stream(nums).toArray(Integer[]::new);

        // 获取流元素组成的 集合,可以传入 集合的构造器
        TreeSet<Integer> collect2 = Arrays.stream(nums).collect(Collectors.toCollection(TreeSet::new));
        // 获取流元素组成的 List
        List<Integer> collect = Arrays.stream(nums).collect(Collectors.toList());
        // 获取流元素组成的 Set
        Set<Integer> collect1 = Arrays.stream(nums).collect(Collectors.toSet());

        // 产生一个整型的对象收集器(收集过程中可以处理元素),利用收集器可以获得 平均、 最大、最小、总和、结果个数
        // 有对应的 long double 版本
        IntSummaryStatistics summaryStatistics = Arrays.stream(nums).collect(Collectors.summarizingInt(n -> n));
        summaryStatistics.getAverage();
        summaryStatistics.getCount();
        summaryStatistics.getMax();
        summaryStatistics.getMin();
        summaryStatistics.getSum();



        String[] words = {"zs","ls","ww"};
        Stream<String> wordsStream = Arrays.stream(words);

        // 可以将 流中的字符串拼接
        String collect3 = wordsStream.collect(Collectors.joining());
        // 设置分隔符
        wordsStream.collect(Collectors.joining(","));
        // 设置分隔符 ,前缀,后缀
        wordsStream.collect(Collectors.joining(",","^","$"));

收集到映射表中 Steam 转 Map
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 流转为 Map ,第一参数是 map 键的获取过程,第二个参数是 map 值的获取过程 ,如果键冲突会抛异常
        //Map<Integer, String> map = stream.collect(Collectors.toMap(n -> n, n -> n.toString()));

        // 第三个参数,指明当键冲突的时候,可以如何处理旧值(原来存在的值)和新值,作为此键对应的值
        Map<Integer, String> map2 = Arrays.stream(nums).collect(Collectors.toMap(n -> n, n -> n.toString(),(oldValue,newValue)->oldValue));

        // 转并发线程安全的 map
        // ConcurrentMap<Integer, String> concurrentMap = Arrays.stream(nums).collect(Collectors.toConcurrentMap(n -> n, n -> n.toString()));


        // 将元素分组,每组保存 到 list 中
        Map<String, List<Integer>> collect = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n>2?"大于2":"小于等于2"));
        System.out.println(collect); // {大于2=[6, 3, 4, 5, 3, 7, 9, 6, 4, 8], 小于等于2=[1, 2, 1]}
        // 取得并发线程安全 map 版
        Map<String, List<Integer>> collect2 = Arrays.stream(nums).collect(Collectors.groupingByConcurrent(n -> n>2?"大于2":"小于等于2"));
        System.out.println(collect2); // {大于2=[6, 3, 4, 5, 3, 7, 9, 6, 4, 8], 小于等于2=[1, 2, 1]}

        // 如果只分两组,可以使用以 boolean 为键的版本,其效率要 比 groupingBy 高
        Map<Boolean, List<Integer>> collect1 = Arrays.stream(nums).collect(Collectors.partitioningBy(n -> n > 2 ? true : false));
        System.out.println(collect1); // {false=[1, 2, 1], true=[6, 3, 4, 5, 3, 7, 9, 6, 4, 8]}
下游收集器, groupingBy 的后续

收集到 Map, groupingBy 的扩展,可对收集到的 list 做进一步转换


        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // toSet() :对 收集到的 map list 型 值,转换成 set
        Map<String, Set<Integer>> collect = stream.collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于等于2", Collectors.toSet()));
        System.out.println(collect); // {大于2=[3, 4, 5, 6, 7, 8, 9], 小于等于2=[1, 2]}

        // summingInt: 对 收集到的 map 各个list 型值 ,分别求各个list中的元素 和 , 这个例子 就是对 大于 2和 小于2 的分别求和 返回到一个 map
        // summingInt  还有对应 double long 版本
        // a->a  这里是将 list 中的数据转成 int 型的过程,因为本来list中的元素就是 int 型,所以不做任何处理返回, 也可以使用 Function.identity() 同样的效果
        Map<String, Integer> collect1 = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2", Collectors.summingInt(a -> a)));
        System.out.println(collect1); // {小于2=4, 大于2=55}

        // counting :对 收集到的 map 各个list 型值 ,分别求各个list中的元素 数目
        Map<String, Long> collect2 = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2", Collectors.counting()));
        System.out.println(collect2); // {小于2=3, 大于2=10}

        // 对 收集到的 map 各个list 型值 ,分别求各个list中的元素 最大元素 对应还有 minBy
        Map<String, Optional<Integer>> collect3 = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2", Collectors.maxBy(Integer::compare)));
        System.out.println(collect3); // {小于2=Optional[2], 大于2=Optional[9]}


        // mapping
        // 对 收集到的 map 各个list 型值 ,分别对各个list中的元素做一层处理后,再交给后续的 Collectors
        Map<String, List<String>> collect4 = Arrays.stream(nums).collect(
                Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2",
                        Collectors.mapping(n -> "c"+n.toString(),
                                Collectors.mapping(n->n,Collectors.toList()))));
        System.out.println(collect4); // {小于2=[c1, c2, c1], 大于2=[c6, c3, c4, c5, c3, c7, c9, c6, c4, c8]}

约简操作 Stream.reduce
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // reduce :和 ConCurrentHashMap.reduce 类似
        // 将前一次的处理结果作为参数,参与下一次处理
        // 即  result = v1 op v2 op v3 op v4 ····  op 必须满足 ,x op y op z = x op ( y op z )
        // op 例 : 求和,乘积,字串拼接,max,min,并集,交集···
        Optional<Integer> reduce = stream.reduce((x, y) -> x + y);

        // 第一个参数为 幺元 e , 幺元满足  e op v = v
        // 当流为 null ,会返回 幺元
        Integer reduce1 = Arrays.stream(nums).reduce(0, (x, y) -> x + y);
        System.out.println(reduce1);

        // 第一个参数为 幺元
        // 第二个参数为 op
        // 第三个参数为,多线程将 不同分组的结果 合并的方式(内部使用了 fork-join框架)
        String reduce2 = Arrays.stream(nums).reduce("x", (x, y) -> x +"-"+ y.toString(), (x, y) -> x +"*" +y);
        System.out.println(reduce2); // x-6-3-1-4-5-3-7-9-6-4-2-1-8

        String reduce4 = Arrays.stream(nums).parallel().reduce("x", (x, y) -> x +"-"+ y.toString(), (x, y) -> x +"*" +y);
        System.out.println(reduce4); // x-6*x-3*x-1*x-4*x-5*x-3*x-7*x-9*x-6*x-4*x-2*x-1*x-8


        Integer reduce3 = Arrays.stream(new String[]{"zs", "ls", "zll"}).parallel().reduce(0, (a, b) -> a + b.length(), (a, b) -> +a + b);
        System.out.println(reduce3); // 7

基本类型流
        // 将基本类型包装成对象使用,效率会降低,可以使用基本类型专属的流,以int为例

        int[] nums={1,6,8,9,7,6};
        IntStream intStream = IntStream.of(1, 3, 6, 4, 9, 8);
        IntStream stream = Arrays.stream(nums);

        OptionalInt first = stream.findFirst();
        OptionalInt max = stream.max();

        IntSummaryStatistics intSummaryStatistics = stream.summaryStatistics();
        intSummaryStatistics.getSum();
        intSummaryStatistics.getMax();
        intSummaryStatistics.getAverage();
        intSummaryStatistics.getCount();

        // 产生步长为 1 的流 ,int 和 lang 专有 ,包括 10
        IntStream range = IntStream.range(1, 10);
        range.forEach(System.out::print);
        // 不包括 10
        IntStream range2 = IntStream.rangeClosed(1, 10);
        range2.forEach(System.out::print);

        int[] ints = stream.toArray();


        // 产生包装器对象流
        Stream<Integer> boxed = stream.boxed();
并行流的获取
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 产生一个 与当前 stream 元素相同的 并行流
        Stream<Integer> parallel = stream.parallel();
        // 产生一个 与当前 stream 元素相同 的无序流
        stream.unordered();

        Collection collection=new ArrayList();

        collection.stream();
        // 根据当前集合产生一个 并行流
        collection.parallelStream();
上一篇下一篇

猜你喜欢

热点阅读