java8 学习

Collectors.toMap:

// toMap 有三个重载的方法
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);

参数说明:

参数说明
keyMapperKey 的映射函数
valueMapperValue 的映射函数
mergeFunction当 Key 冲突时,调用的合并方法
mapSupplierMap 构造器,在需要返回特定的 Map 时使用

使用toMap 映射map集合 key重复会报下面的错

List<User> userList = Lists.newArrayList(
        new User().setId("A").setName("张三"),
        new User().setId("A").setName("李四"), // Key 相同 
        new User().setId("C").setName("王五")
);
userList.stream().collect(Collectors.toMap(User::getId, User::getName));

// 异常:
java.lang.IllegalStateException: Duplicate key 张三 
    at java.util.stream.Collectors.lambda$throwingMerger$114(Collectors.java:133)
    at java.util.HashMap.merge(HashMap.java:1245)
    at java.util.stream.Collectors.lambda$toMap$172(Collectors.java:1320)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at Test.toMap(Test.java:17)

第三个参数就是为了 防止重复 进行合并

userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2));

// 输出结果:
A-> 张三李四 
C-> 王五 

第四个参数(mapSupplier)用于自定义返回 Map 类型,比如我们希望返回的 Map 是根据 Key 排序的,可以使用如下写法:

List<User> userList = Lists.newArrayList(
        new User().setId("B").setName("张三"),
        new User().setId("A").setName("李四"),
        new User().setId("C").setName("王五")
);
userList.stream().collect(
    Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1, TreeMap::new)
);

// 输出结果:
A-> 李四 
B-> 张三 
C-> 王五 

Collectors.groupingBy:

public Product(Long id, Integer num, BigDecimal price, String name, String category) {
	this.id = id;
	this.num = num;
	this.price = price;
	this.name = name;
	this.category = category;
}

Product prod1 = new Product(1L, 1, new BigDecimal("15.5"), "面包", "零食");
Product prod2 = new Product(2L, 2, new BigDecimal("20"), "饼干", "零食");
Product prod3 = new Product(3L, 3, new BigDecimal("30"), "月饼", "零食");
Product prod4 = new Product(4L, 3, new BigDecimal("10"), "青岛啤酒", "啤酒");
Product prod5 = new Product(5L, 10, new BigDecimal("15"), "百威啤酒", "啤酒");
List<Product> prodList = Lists.newArrayList(prod1, prod2, prod3, prod4, prod5);
  • 分组
  1. 按照类别分组:
Map<String, List<Product>> prodMap= prodList.stream().collect(Collectors.groupingBy(Product::getCategory));

// 输出
{
"啤酒":[{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10},{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}],
"零食":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5},{"category":"零食","id":2,"name":"饼干","num":2,"price":20},{"category":"零食","id":3,"name":"月饼","num":3,"price":30}]
}
  1. 按照几个属性拼接分组:
Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "_" + item.getName()));
// 输出
{
"零食_月饼":[{"category":"零食","id":3,"name":"月饼","num":3,"price":30}],
"零食_面包":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5}],
"啤酒_百威啤酒":[{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}],
"啤酒_青岛啤酒":[{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10}],
"零食_饼干":[{"category":"零食","id":2,"name":"饼干","num":2,"price":20}]
}
  1. 根据不同条件分组:
Map<String, List<Product>> prodMap= prodList.stream().collect(Collectors.groupingBy(item -> {
	if(item.getNum() < 3) {
		return "3";
	}else {
		return "other";
	}
}));

// 输出
{
"other":[{"category":"零食","id":3,"name":"月饼","num":3,"price":30},
		 {"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10},
		 {"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}],
"3":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5},
	 {"category":"零食","id":2,"name":"饼干","num":2,"price":20}]
}
  1. 多级分组
    要实现多级分组,我们可以使用一个由双参数版本的Collectors.groupingBy工厂方法创 建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。那么要进 行二级分组的话,我们可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流 中项目分类的二级标准。
Map<String, Map<String, List<Product>>> prodMap= prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.groupingBy(item -> {
	if(item.getNum() < 3) {
		return "3";
	}else {
		return "other";
	}
})));
//输出
{
"啤酒":
	{"other":[{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10},
			{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}]},
"零食":
	{"other":[{"category":"零食","id":3,"name":"月饼","num":3,"price":30}],
	 "3":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5},{"category":"零食","id":2,"name":"饼干","num":2,"price":20}]}}
  • 按子组收集数据
  1. 求总数
Map<String, Long> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));

//{"啤酒":2,"零食":3}
  1. 求和
Map<String, Integer> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));

//{"啤酒":13,"零食":6}

  1. 把收集器的结果转换为另一种类型
Map<String, Product> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get)));

//输出
{"啤酒":{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15},
 "零食":{"category":"零食","id":3,"name":"月饼","num":3,"price":30}}
  1. 联合其他收集器
Map<String, Set<String>> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));

//{"啤酒":["青岛啤酒","百威啤酒"],"零食":["面包","饼干","月饼"]}

Collectors.collectingAndThen:

一般是根据 分组来进行类型转换

有一个List,数据库中查询出来的列表,需要聚合并转换类型为一个map:

Map<String, Map<Integer, RecipeCalendarInfoVO>>

这里的key是日期(RecipeCalendar.recipeDate格式yyyy-MM-dd),value是当日(早,中,晚)的食谱区间map;
食谱区间map(Map<Integer, RecipeCalendarInfoVO>)的key是10.早餐 20.中餐 30.晚餐(RecipeCalendar.intervalType), value是实际的食谱详细信息(RecipeCalendarInfoVO(内容参考类信息))

RecipeCalendar类(省略get,set方法):

@Data
public class RecipeCalendar extends Model<RecipeCalendar> {
 
    private static final long serialVersionUID = 1L;
 
    /**
     * 食谱日历 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
 
    /**
     * 患者唯一编号
     */
    private Long patientFileId;
 
    /**
     * 租户ID
     */
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
 
    /**
     * 员工id
     */
    private Long employeeId;
 
    /**
     * 员工姓名
     */
    private String employeeName;
 
    /**
     * 食谱ID
     */
    private Long recipeId;
 
    /**
     * 日期区间 10.早餐 20.中餐 30.晚餐
     */
    private Integer intervalType;
 
    /**
     * 食谱日历日期 格式 YYYY-MM-DD,范围 1000-01-01/9999-12-31
     */
    private Date recipeDate;
 
    /**
     * 食谱名称
     */
    private String recipeName;
 
    /**
     * 食谱热量(kcal)
     */
    private Integer calories;
}

RecipeCalendarInfoVO类(省略get,set方法):

@Data
public class RecipeCalendarInfoVO{
	@ApiModelProperty("食谱总热量(sum(RecipeCalendar.calories))")
	private Integer calories;
 
	@ApiModelProperty("食谱列表")
	private List<RecipeCalendar> recipeCalendarList;
}

聚合并转换类型(使用Collectors.groupingBy和Collectors.collectingAndThen):

// 集合不为空
List<RecipeCalendar> list;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
// 数据聚合转换
Map<String, Map<Integer, RecipeCalendarInfoVO>> recipeCalendarDateMap = list.stream()
		.collect(
			// 根据日期聚合
			Collectors.groupingBy(recipeCalendar -> simpleDateFormat.format(recipeCalendar.getRecipeDate()),
				// 保持顺序,用LinkedHashMap::new
				LinkedHashMap::new,
				// 根据日期区间(早,中,晚)聚合第二层
				Collectors.groupingBy(RecipeCalendar::getIntervalType,
					// 保持顺序,用LinkedHashMap::new
					LinkedHashMap::new,
					// 处理内层list 转换为 RecipeCalendarInfoVO
					Collectors.collectingAndThen(Collectors.toList(), recipeCalendars -> {
						RecipeCalendarInfoVO recipeCalendarInfoVO = new RecipeCalendarInfoVO();
						recipeCalendarInfoVO.setCalories(recipeCalendars.stream().mapToInt(recipeCalendar -> recipeCalendar.getCalories() == null ? 0 : recipeCalendar.getCalories()).sum());
						recipeCalendarInfoVO.setRecipeCalendarList(recipeCalendars);
						return recipeCalendarInfoVO;
					}
		))));
return recipeCalendarDateMap;

List对象中的属性以逗号分隔转字符串

String names = testDemos.stream().map(TestDemo::getName).collect(Collectors.joining(","));

在这里插入图片描述

每天努力一点点
文章自己网上整理,如有侵权及时删除

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值