Java8 Stream流的终端操作

  • 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> op 即R 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, korea  Apple, 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?

欢迎在评论区留下你看文章时的思考,及时说出,有助于加深记忆和理解,还能和像你一样也喜欢这个话题的读者相遇~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值