Java8之用流收集数据

Java8之用流收集数据

第六章 用流收集数据

1、 toList()

  • 用法
List<Transaction> transactions =
 				transactionStream.collect(Collectors.toList());

2、归约和汇总

1)count/counting
long howManyDishes = menu.stream().collect(Collectors.counting());
//这还可以写得更为直接:
long howManyDishes = menu.stream().count();
2)找流中的最大值和最小值 --maxBy
  • Collectors.maxBy
Comparator<Dish> dishCaloriesComparator =
 					Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish =
                 menu.stream()
                 			.collect(maxBy(dishCaloriesComparator));
3)汇总/平均数Collectors.summingInt等
  • Collectors.summingInt 求出菜单列表的总热量:
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

Collectors.summingLong和Collectors.summingDouble方法的作用完全一样,可以用 于求和字段为long或double的情况。

  • Collectors.averagingInt,连同对应的averagingLong和averagingDouble可以计算数值的平均数
double avgCalories =
 			menu.stream().collect(averagingInt(Dish::getCalories));
  • 用summarizingInt工厂 方法返回的收集器 , 通过一次summarizing操作你可以就数出菜单中元素的个数,并得 到菜肴热量总和、平均值、最大值和最小值:
IntSummaryStatistics menuStatistics =
			 menu.stream().collect(summarizingInt(Dish::getCalories));
  • 结果

IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}

4) 连接字符串 -- joining
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));
  • 结果

pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon

3、分组-- Collectors.groupingBy

Map<Dish.Type, List<Dish>> dishesByType =
 			menu.stream().collect(groupingBy(Dish::getType));
  • 结果

{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza], MEAT=[pork, beef, chicken]}

  • 复杂一点的
public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = 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)多级分组
  • 要进 行二级分组的话,我们可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流 中项目分类的二级标准
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
            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;
 									} )
 						)
				);
  • 结果

{MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]}, FISH={DIET=[prawns], NORMAL=[salmon]}, OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}}

一般来说,把groupingBy看作“桶”比较容易明白。第一个groupingBy给每个键建立了 一个桶。然后再用下游的收集器去收集每个桶中的元素,以此得到n级分组。

1607848004762

4、 分区

分区是分组的特殊情况:由一个谓词(返回一个布尔值的函数)作为分类函数,它称分区函 数。分区函数返回一个布尔值,这意味着得到的分组Map的键类型是Boolean,于是它最多可以 分为两组——true是一组,false是一组。

Map<Boolean, List<Dish>> partitionedMenu =
				 menu.stream().collect(partitioningBy(Dish::isVegetarian));
List<Dish> vegetarianDishes =
 				menu.stream().filter(Dish::isVegetarian).collect(toList());

{false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}

1) 分区的优势
  • partitioningBy 工厂方法有一个重载版本,可以像下面这样传递第二个收集器:
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
                  menu.stream().collect(
                                    partitioningBy(Dish::isVegetarian,
                                                   groupingBy(Dish::getType)));

{false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]}, true={OTHER=[french fries, rice, season fruit, pizza]}}

  • 找到素食和非素 食中热量最高的菜:
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
        menu.stream().collect(
                     partitioningBy(Dish::isVegetarian,
                     collectingAndThen(
                               maxBy(comparingInt(Dish::getCalories)),
                               Optional::get)));

{false=pork, true=pizza}

*** Collectors类静态工厂方法的表格 ***

image-20201217094750459

image-20201217094854828

5、收集器接口

1) 自己的ToListCollector方法
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.*;

public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }
    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }
    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.indentity();
    }
    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }
    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(
                IDENTITY_FINISH, CONCURRENT));
    }
}

Q.E.D.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水豚少年的码农生活

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值