Java8 stream分组按某字段取最大值

场景

项目中有这样的场景:

  • 一个商品可以参与多个类型的促销活动,如满减、满赠、买赠、优惠券等活动;
  • 相同类型的活动也可能有多个;
  • 每个活动根据类型和具体的业务字段有一个活动标签,如满减活动,消费满200元减20元,活动标签为满200减20
  • 商品列表的界面上需要展示每个商品的促销活动标签,相同类型活动有多个只展示最新一个创建的活动标签
  • 假定活动id是递增的,新创建的活动id值更大

促销活动类型定义:

@Getter
@AllArgsConstructor
private static enum PromotionType {
    FULL_MINUS("FULL_MINUS", "满减"),
    FULL_GIFT("FULL_GIFT", "满赠"),
    BUY_GIFT("BUY_GIFT", "买赠"),
    COUPON("COUPON", "优惠券");

    private String name;
    private String description;
}

促销活动模型vo定义:

@NoArgsConstructor
@AllArgsConstructor
@Data
private static class PromotionActivityVo implements Serializable {

    // 活动id
    private Integer id;

    // 活动类型
    private PromotionType type;

    // 活动标签
    private String label;
}

测试用例:

// 假定某商品参与了如下5个促销活动
PromotionActivityVo activity1 = new PromotionActivityVo(1, PromotionType.FULL_MINUS, "满200减20");
PromotionActivityVo activity2 = new PromotionActivityVo(2, PromotionType.FULL_MINUS, "满300减30");
PromotionActivityVo activity3 = new PromotionActivityVo(3, PromotionType.BUY_GIFT, "买180赠10");
PromotionActivityVo activity4 = new PromotionActivityVo(4, PromotionType.FULL_GIFT, "满150赠5");
PromotionActivityVo activity5 = new PromotionActivityVo(5, PromotionType.FULL_GIFT, "满300赠25");
List<PromotionActivityVo> activities = Lists.newArrayList(activity1, activity2, activity3, activity4, activity5);

预期输出结果为:
满300减30, 满300赠25, 买180赠10

注:

  • 按促销活动类型里定义的顺序输出
  • 输出第2个满减活动和第5个满赠活动因为同类型的活动它们的id值更大
  • 优惠券活动没有参与,因此没有该活动的促销标签

思路

  1. 将促销活动列表按促销活动类型分组生成一个map,其中key为促销活动类型,value为活动模型vo
  2. 将促销活动类型转为stream然后根据map取值,过滤掉空值,转换为label的列表

实现

Map<PromotionType, PromotionActivityVo> promotionTypeMap = activities.stream().collect(Collectors.groupingBy(PromotionActivityVo::getType
                , Collectors.collectingAndThen(Collectors.reducing((o1, o2) ->
                        Long.valueOf(o1.getId()).compareTo(Long.valueOf(o2.getId())) > 0 ? o1 : o2), Optional::get)));
List<String> labels = Arrays.stream(PromotionType.values()).map(promotionTypeMap::get).filter(Objects::nonNull).map(PromotionActivityVo::getLabel).collect(Collectors.toList());
System.out.println(labels);

其中第1步转换map可用另一种方式:

Map<PromotionType, PromotionActivityVo> promotionTypeMap = activities.stream().collect(Collectors.toMap(PromotionActivityVo::getType, Function.identity(), (o1, o2) -> Long.valueOf(o1.getId()).compareTo(Long.valueOf(o2.getId())) > 0 ? o1 : o2));

输出结果为:[满300减30, 满300赠25, 买180赠10]

参考

posted @ 2021-09-23 22:57  cdfive  阅读(8762)  评论(0编辑  收藏  举报