前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java8 Stream流的终端操作

Java8 Stream流的终端操作

作者头像
喜欢天文的pony站长
发布2020-10-09 15:31:02
6910
发布2020-10-09 15:31:02
举报
文章被收录于专栏:RabbitMQ实战
  • Java8提供的Stream支持两种操作
    • 一种是中间操作,如filter, map, skip, limit...
    • 另外一种是终端操作,如count, findFirst, forEach和reduce...

中间操作不会消耗流,只是将一个流转换成另外一个流,类似于流水线。

终端操作会消耗流,以产生一个最终结果,终端操作完成后,流就被消耗了,不可再调用相关操作流的方法。

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

Collectors终端操作

一、 规约与汇总

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

代码语言:javascript
复制
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. 求和,计算平均值与结果收集器

代码语言:javascript
复制
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);
}
  • 结果:
代码语言:javascript
复制
求和结果: 514905
平均值为: 514.905
  • 在需要同时获取流中元素的个数,求和,平均值,最大值,最小值时,可使用收集器XxxSummaryStatistics
代码语言:javascript
复制
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);
  • 结果:
代码语言:javascript
复制
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)

代码语言:javascript
复制
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(",", "", "")));
}
  • 结果
代码语言:javascript
复制
【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),接收两个参数,并返回经过处理之后的值。
代码语言:javascript
复制
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进行分组。
代码语言:javascript
复制

/**
 * 质量标准
 */
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));
}
  • 结果:
代码语言:javascript
复制
{
  "NORMAL": [
    {
      "country": "韩国",
      "weight": 30
    },
    {
      "country": "日本",
      "weight": 40
    }
  ],
  "PERFECT": [
    {
      "country": "中国",
      "weight": 10
    },
    {
      "country": "米国",
      "weight": 20
    }
  ]
}
  • 多级分组
    • 先根据质量进行分组,再根据国家进行分组
代码语言:javascript
复制
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));
}
  • 结果
代码语言:javascript
复制
{
  "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()方法其实:
代码语言:javascript
复制
    public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier) {
        return groupingBy(classifier, toList());
    }

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

代码语言:javascript
复制
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));
}
  • 结果:
代码语言:javascript
复制
{
  "PERFECT": 2,
  "NORMAL": 6
}
  • demo2: 先按质量分组,将子组List中的国家名字拼成String打印出来。
代码语言:javascript
复制
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));
}
  • 结果:
代码语言:javascript
复制
{
  "PERFECT": "中国,米国",
  "NORMAL": "韩国,日本"
}
  • demo3: 将子组的结果转换成另外一种格式collectingAndThen()

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

代码语言:javascript
复制
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));
}
  • 结果:
代码语言:javascript
复制
{
    "NORMAL":40,
    "PERFECT":20
}

三、分区 partitioningBy()

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

代码语言:javascript
复制
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));
}
  • 结果:
代码语言:javascript
复制
{
  "false": "中国,米国",
  "true": "韩国,日本"
}
> 如何自定义Collector?

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 喜欢天文 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 规约与汇总
  • 二、 分组
  • 三、分区 partitioningBy()
    • > 如何自定义Collector?
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档