美文网首页
Java8 Stream流的终端操作

Java8 Stream流的终端操作

作者: 垃圾简书_吃枣药丸 | 来源:发表于2020-09-22 18:41 被阅读0次
    • Java8提供的Stream支持两种操作
      • 一种是中间操作,如filter, map, skip, limit...
      • 另外一种是终端操作,如count, findFirst, forEach和reduce...

    中间操作不会消耗流,只是将一个流转换成另外一个流,类似于流水线。
    终端操作会消耗流,以产生一个最终结果,终端操作完成后,流就被消耗了,不可再调用相关操作流的方法。

    • Collectors预定义好的部分终端操作
    Collectors终端操作

    一、 规约与汇总

    1. 查找流中的最大值和最小值

    public static void main(String[] args) {
        Random random = new Random();
        Optional<Integer> minVal = Stream.generate(() -> random.nextInt(1000))
                .limit(100)
                .collect(Collectors.minBy(Comparator.comparingInt(x -> x)));
    
        minVal.ifPresent(System.out::println);
    
        Optional<Integer> maxVal = Stream.generate(() -> random.nextInt(1000))
                .limit(100)
                .collect(Collectors.maxBy(Comparator.comparingInt(x -> x)));
        maxVal.ifPresent(x -> System.out.println("生成的最大随机值为: " + x));
    
        //或或或或或或或或或或或或或或或或或或或或
        Optional<Integer> maxVal2 = Stream.generate(() -> random.nextInt(1000))
                .limit(100)
                .max(Comparator.comparingInt(x -> x));
    }
    

    2. 求和,计算平均值与结果收集器

    public void sumAvg() {
        Random random = new Random();
        // 生成1000范围内的数字的方法
        Supplier<Integer> integerSupplier = () -> random.nextInt(1000);
        // 生成1000个1000以内的数字,并返回一个列表
        List<Integer> integerList = Stream.generate(integerSupplier)
                .limit(1000)
                .collect(Collectors.toList());
    
        // 求和操作
        Integer sum = integerList.stream()
                .collect(Collectors.summingInt(x -> x));
        System.out.println("求和结果: " + sum);
    
        // 计算平均值操作
        Double avg = integerList.stream()
                .collect(Collectors.averagingDouble(x -> x));
        System.out.println("平均值为: " + avg);
    }
    
    • 结果:
    求和结果: 514905
    平均值为: 514.905
    
    • 在需要同时获取流中元素的个数,求和,平均值,最大值,最小值时,可使用收集器XxxSummaryStatistics
    DoubleSummaryStatistics summaryStatistics = integerList.stream()
            .collect(Collectors.summarizingDouble(x -> x));
    
    long count = summaryStatistics.getCount();
    double average = summaryStatistics.getAverage();
    double max = summaryStatistics.getMax();
    double min = summaryStatistics.getMin();
    double sumResult = summaryStatistics.getSum();
    
    System.out.println(count);
    System.out.println(average);
    System.out.println(max);
    System.out.println(min);
    System.out.println(sumResult);
    // XxxSummaryStatistics重写了toString()方法
    System.out.println(summaryStatistics);
    
    • 结果:
    1000
    514.905
    999.0
    0.0
    514905.0
    DoubleSummaryStatistics{count=1000, sum=514905.000000, min=0.000000, average=514.905000, max=999.000000}
    

    3. 连接并返回字符串Collectors.join(delimiter)

    public void joinDemo() {
        AppleStream chinaApple = new AppleStream(10, "中国");
        AppleStream usApple = new AppleStream(20, "米国");
        AppleStream koreaApple = new AppleStream(30, "韩国");
    
        String joinResult = Stream.of(chinaApple, usApple, koreaApple)
                // 需要将流转换成Stream<String>
                .map(AppleStream::toString)
                .collect(Collectors.joining(",", "【", "】"));
        System.out.println(joinResult);
    
        System.out.println(IntStream.rangeClosed(1, 20)
                .mapToObj(String::valueOf)
                .collect(Collectors.joining(",", "", "")));
    }
    
    • 结果
    【AppleStream(weight=10, country=中国),AppleStream(weight=20, country=米国),AppleStream(weight=30, country=韩国)】
    1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
    

    4. 自定义规约操作:广义上的规约汇总

    • Collectors.reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)
      • 参数U identity 规约起始值,当流中没有元素时也会返回这个值。
      • 参数Function<? super T, ? extends U> mapper 在执行参数3方法之前对流中的元素进行的操作。
      • 参数BinaryOperator<U> opR apply(T t, U u),接收两个参数,并返回经过处理之后的值。
    public void reduceDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple usApple = new Apple(20, "米国");
        Apple koreaApple = new Apple(30, "韩国");
    
        //重量最大的苹果
        Optional<Apple> maxWeightApple = Stream.<Apple>of(chinaApple, usApple, koreaApple)
                .collect(Collectors.reducing((x1, x2) -> x1.getWeight() > x1.getWeight() ? x1 : x2));
        maxWeightApple.ifPresent(System.out::println);
    
        // 最小的重量
        Integer minVal = Stream.of(chinaApple, usApple, koreaApple)
                .collect(Collectors.reducing(0, Apple::getWeight, Integer::min));
        System.out.println(minVal);
    
        // 质量总和
        Integer sumVal = Stream.of(chinaApple, usApple, koreaApple)
                .collect(Collectors.reducing(0, Apple::getWeight, Integer::sum));
        System.out.println(sumVal);
    }
    

    二、 分组

    • Collectors.groupingBy()
      1. 分组
      • demo(一级分组): 根据规则 质量<=20为优秀,质量>20为一般 将apple进行分组。
    
    /**
     * 质量标准
     */
    enum Quality {
        /**
         * 上乘
         */
        PERFECT,
        /**
         * 普通
         */
        NORMAL;
    }
    
    public void groupByDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple usApple = new Apple(20, "米国");
        Apple koreaApple = new Apple(30, "韩国");
        Apple japanApple = new Apple(40, "日本");
    
        Map<Quality, List<Apple>> appleQualityMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
                .collect(Collectors.groupingBy(curApple -> {
                    // 质量<=20为优秀,质量>20为一般
                    if (curApple.getWeight() <= 20) {
                        return Quality.PERFECT;
                    } else {
                        return Quality.NORMAL;
                    }
                }));
    
        System.out.println(JSON.toJSONString(appleQualityMap, true));
    }
    
    • 结果:
    {
      "NORMAL": [
        {
          "country": "韩国",
          "weight": 30
        },
        {
          "country": "日本",
          "weight": 40
        }
      ],
      "PERFECT": [
        {
          "country": "中国",
          "weight": 10
        },
        {
          "country": "米国",
          "weight": 20
        }
      ]
    }
    
    • 多级分组
      • 先根据质量进行分组,再根据国家进行分组
    public void groupByDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple chinaAppleEnhance = new Apple(100, "中国");
        Apple chinaAppleDoubleEnhance = new Apple(1000, "中国");
    
        Apple usApple = new Apple(20, "米国");
    
        Apple koreaApple = new Apple(30, "韩国");
    
        Apple japanApple = new Apple(40, "日本");
        Apple japanAppleEnhance = new Apple(80, "日本");
        Apple japanAppleDoubleEnhance = new Apple(120, "日本");
    
        Map<Quality, Map<String, List<Apple>>> appleQualityMap = Stream.of(chinaApple, chinaAppleEnhance, chinaAppleDoubleEnhance, usApple, koreaApple, japanApple, japanAppleEnhance, japanAppleDoubleEnhance)
                .collect(Collectors.groupingBy(curApple -> {
                    // 质量<=20为优秀,质量>20为一般
                    if (curApple.getWeight() <= 20) {
                        return Quality.PERFECT;
                    } else {
                        return Quality.NORMAL;
                    }
                    // 再根据质量进行分组后再根据国家进行分组
                }, Collectors.groupingBy(Apple::getCountry)));
    
    
        System.out.println(JSON.toJSONString(appleQualityMap, true));
    }
    
    • 结果
    {
      "NORMAL": {
        "韩国": [
          {
            "country": "韩国",
            "weight": 30
          }
        ],
        "中国": [
          {
            "country": "中国",
            "weight": 100
          },
          {
            "country": "中国",
            "weight": 1000
          }
        ],
        "日本": [
          {
            "country": "日本",
            "weight": 40
          },
          {
            "country": "日本",
            "weight": 80
          },
          {
            "country": "日本",
            "weight": 120
          }
        ]
      },
      "PERFECT": {
        "米国": [
          {
            "country": "米国",
            "weight": 20
          }
        ],
        "中国": [
          {
            "country": "中国",
            "weight": 10
          }
        ]
      }
    }
    
    • 注意:可以无限叠加层N层Map哟。

    2. 按子组收集数据(指定用于处理子组数据的函数)

    • 观察源码发现,我们使用最多的接收一个参数的groupingBy()方法其实:
        public static <T, K> Collector<T, ?, Map<K, List<T>>>
        groupingBy(Function<? super T, ? extends K> classifier) {
            return groupingBy(classifier, toList());
        }
    

    默认第二个参数是Collectors.toList(),实际上可以替换成我们需要的方法,如计算子组的数量:

    public void groupByDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple chinaAppleEnhance = new Apple(100, "中国");
        Apple chinaAppleDoubleEnhance = new Apple(1000, "中国");
    
        Apple usApple = new Apple(20, "米国");
    
        Apple koreaApple = new Apple(30, "韩国");
    
        Apple japanApple = new Apple(40, "日本");
        Apple japanAppleEnhance = new Apple(80, "日本");
        Apple japanAppleDoubleEnhance = new Apple(120, "日本");
    
        Map<Quality, Long> qualityLongMap = Stream.of(chinaApple, chinaAppleEnhance, chinaAppleDoubleEnhance, usApple, koreaApple, japanApple, japanAppleEnhance, japanAppleDoubleEnhance)
                .collect(Collectors.groupingBy(curApple -> {
                    // 质量<=20为优秀,质量>20为一般
                    if (curApple.getWeight() <= 20) {
                        return Quality.PERFECT;
                    } else {
                        return Quality.NORMAL;
                    }
                    // 再根据质量进行分组后再计算每组的元素的个数
                }, Collectors.counting()));
        System.out.println(JSON.toJSONString(qualityLongMap, true));
    }
    
    • 结果:
    {
      "PERFECT": 2,
      "NORMAL": 6
    }
    
    • demo2: 先按质量分组,将子组List中的国家名字拼成String打印出来。
    public void groupByDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple usApple = new Apple(20, "米国");
        Apple koreaApple = new Apple(30, "韩国");
        Apple japanApple = new Apple(40, "日本");
    
        Map<Quality, String> qualityCountryMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
                .collect(Collectors.groupingBy(curApple -> {
                    // 质量<=20为优秀,质量>20为一般
                    if (curApple.getWeight() <= 20) {
                        return Quality.PERFECT;
                    } else {
                        return Quality.NORMAL;
                    }
                    // 再将相应分组的国家的名字打印出来
                }, Collectors.mapping(Apple::getCountry, Collectors.joining(","))));
        System.out.println(JSON.toJSONString(qualityCountryMap, true));
    }
    
    • 结果:
    {
      "PERFECT": "中国,米国",
      "NORMAL": "韩国,日本"
    }
    
    • demo3: 将子组的结果转换成另外一种格式collectingAndThen()

    先要求: 先根据apple的weight进行分组,再将每组中weight最大的apple找出来,再返回每组最大的apple的weight,即返回Map<quality, maxWeight>

    public void groupByDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple usApple = new Apple(20, "米国");
        Apple koreaApple = new Apple(30, "韩国");
        Apple japanApple = new Apple(40, "日本");
    
        Map<Quality, Integer> qualityCountryMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
                .collect(Collectors.groupingBy(curApple -> {
                    // 质量<=20为优秀,质量>20为一般
                    if (curApple.getWeight() <= 20) {
                        return Quality.PERFECT;
                    } else {
                        return Quality.NORMAL;
                    }
                }, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Apple::getWeight)), (optionalApple) -> optionalApple.get().getWeight())));
    
        System.out.println(JSON.toJSONString(qualityCountryMap, true));
    }
    
    • 结果:
    {
        "NORMAL":40,
        "PERFECT":20
    }
    

    三、分区 partitioningBy()

    分区是分组的一种特殊情况,分区返回的Map的keys只有truefalse。其他用法与groupingBy基本一致。

    public void partitioningByDemo() {
        Apple chinaApple = new Apple(10, "中国");
        Apple usApple = new Apple(20, "米国");
        Apple koreaApple = new Apple(30, "韩国");
        Apple japanApple = new Apple(40, "日本");
    
        Map<Boolean, String> appleQualityCountryMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
                //以质量20为分界线进行分区,将分区之后的apple的原产地的国家返回
                .collect(Collectors.partitioningBy(x -> x.getWeight() > 20, Collectors.mapping(Apple::getCountry, Collectors.joining(","))));
        System.out.println(JSON.toJSONString(appleQualityCountryMap, true));
    }
    
    • 结果:
    {
      "false": "中国,米国",
      "true": "韩国,日本"
    }
    

    > 如何自定义Collector?

    相关文章

      网友评论

          本文标题:Java8 Stream流的终端操作

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