具体地说,流收集器是对流调用collect方法将对流中的元素触发一个归约操作( 由Collector来参数化)。

归约和汇总

计数counting

1
2
3
// 计算菜单中菜的总数
menu.stream().collect(Collectors.counting())
menu.stream().count()

最大值和最小值

  • 最大值maxBy

    1
    2
    
    menu.stream().collect(Collectors.maxBy(Comparator
    .comparingInt(Dish::getCalories)));
    1
    2
    3
    4
    
    //使用reduceing实现相同功能
    menu.stream().collect(Collectors
    .reducing((Dish d1, Dish d2) -> d1.getCalories() > 
    d2.getCalories() ? d1 : d2));
  • 最小值 minBy

    1
    2
    3
    
    menu.stream().collect(Collectors.minBy(
     Comparator.comparingInt(Dish::getCalories)
    ));

汇总

  • summingInt

    1
    
    menu.stream().collect(Collectors.summingInt(Dish::getCalories)
    1
    2
    3
    4
    5
    6
    7
    
    //使用reduceing实现相同功能
    //方式一
    menu.stream().collect(Collectors.reducing(0, Dish::getCalories, 
     (a, b) -> a + b));
    // 方式二 
    menu.stream().collect((Collectors.reducing(0, 
     Dish:: getCalories, Integer:: sum)));
  • summarizingInt得到总和、平均值、最大值、最小值

    1
    
    menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));

输出结果为:

IntSummaryStatistics{count=9, sum=4200, min=120, average=466.666667, max=800}

相应的summarizingLongsummarizingDouble工厂方法对应相关的LongSummaryStatisticsDoubleSummaryStatistics类型。

连接字符串

1
2
3
4
5
6
//默认空格连接
menu.stream().map(Dish::getName).collect(Collectors.joining());
// ,连接
menu.stream().map(Dish::getName).collect(Collectors.joining(","))
 // reduce方式连接, 返回Optinal
menu.stream().map(Dish::getName).reduce(String::concat);

joining 内部使用StringBuilder连接字符串。

Collectors.reducing

Collectors.reducing工厂方法是所有归约汇总方法的一般化,即上述所有功能都可通过reducing实现。

reducing方法接收三个参数

  • 第一个参数,归约操作的起始值
  • 第二个参数,Function<T, R>类型
  • 第三个参数,BinaryOperator<U>类型

分组

简单分组

1
2
3
//根据菜的类型分组
Map<Dish.Type, List<Dish>> dishByType = menu.stream()
    .collect(Collectors.groupingBy(Dish::getType));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public enum CaloricLevel{
    DIET,NORMAL,FAT
}

//根据卡路里情况分组
Map<CaloricLevel, List<Dish>> dishByCaloric = menu.stream()
    .collect(Collectors.groupingBy(dish -> {
        if (dish.getCalories() <= 400 ){
            return CaloricLevel.DIET;
        }else if (dish.getCalories() <= 700) {
            return  CaloricLevel.NORMAL;
        }else {
            return CaloricLevel.FAT;
        }
    }));

多级分组

  • 根据类型和卡路里分组

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishByTypeCaloricLevel = menu.stream().collect(
           Collectors.groupingBy(Dish::getType,
                   Collectors.groupingBy(dish -> {
                       if (dish.getCalories() <= 400 ){
                           return CaloricLevel.DIET;
                       }else if (dish.getCalories() <= 700) {
                           return  CaloricLevel.NORMAL;
                       }else {
                           return CaloricLevel.FAT;
                       }
                   })
           )
        );
  • 按类型分组并计算每组数量

    1
    2
    
    Map<Dish.Type, Long> typesCount = menu.stream().
    collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
  • 按类型分组并查找卡路里最大的

    1
    2
    3
    4
    5
    6
    7
    8
    
    //返回Optional类型
    Map<Dish.Type, Optional<Dish>> typesMostCaloriesOP = menu.stream()
     .collect( Collectors.groupingBy(Dish::getType,
            Collectors.maxBy(
                Comparator.comparingInt(Dish:: getCalories)
            )
     )
    );

把收集器结果转换为另一种类型:Collectors.collectingAndThen

1
2
3
4
5
6
7
8
//返回Dish类型
Map<Dish.Type, Dish> typesMostCalories = menu.stream()
    .collect(Collectors.groupingBy(Dish::getType,
         Collectors.collectingAndThen(
              Collectors.maxBy(
                  Comparator.comparingInt(Dish:: getCalories)),
             Optional::get))
        );
  • 按类型分组并计算热量总和

    1
    2
    3
    
    Map<Dish.Type, Integer> typesCaloriesCount = menu.stream().
    collect(Collectors.groupingBy(Dish::getType,
                Collectors.summingInt(Dish::getCalories)));
  • 按类型分组并映射为卡路里级别Collectors.mapping

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    Map<Dish.Type, Set<CaloricLevel>> caloricLevelByType = menu.stream().
    collect(Collectors.groupingBy(Dish::getType, 
              Collectors.mapping((Dish dish) -> {
                    if (dish.getCalories() <= 400 ){
                        return CaloricLevel.DIET;
                    }else if (dish.getCalories() <= 700) {
                        return  CaloricLevel.NORMAL;
                    }else {
                        return CaloricLevel.FAT;
                    }
                }, Collectors.toCollection(HashSet::new)/*Collectors.toSet()*/)));

分区

由一个谓词(返回一个布尔值的函数)作为分类函数,称为分区函数。意味着分组Map的键类型为Boolean,最多可分两组——true一组,false一组。

  • 按照是否是素菜分区

    1
    2
    
    Map<Boolean, List<Dish>> partitionedMenu = menu.stream().
    collect(Collectors.partitioningBy(Dish:: isVegetarian));
  • 按照是否是素菜分区并按类型分组

    1
    2
    3
    
    Map<Boolean, Map<Dish.Type, List<Dish>>> partitionedMenuGroupByType = menu.stream().collect(
     Collectors.partitioningBy(Dish::isVegetarian,
     Collectors.groupingBy(Dish::getType))
  • 数字按质数和非质数分区

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    public static boolean isPrime(int candidate){
    int candidateRoot = (int) Math.sqrt((double) candidate);
    return IntStream.rangeClosed(2, candidateRoot)
        .noneMatch(i -> candidate % i == 0);
    }
    
    public static Map<Boolean, List<Integer>> partitionPrimes(int n){
    return IntStream.rangeClosed(2, n)
        .boxed()
        .collect(Collectors.partitioningBy(candidate -> isPrime(candidate)));
    }
    partitionPrimes(20)

本文为学习记录,因能力有限,如有错误请赐教……如需转载,请注明出处!