You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Java Stream按多属性分组聚合min和max值的实现问题

解决方案:单流处理完成分组+归约+转换

直接通过一次Stream操作,结合分组与归约逻辑,完成多属性分组、字段求和及目标对象转换,避免繁琐的多次流处理。

前提假设

先明确相关类的结构(可根据实际调整):

// 原始数据类
class OccupancyEntry {
    private String attribute1;
    private String attribute2;
    private String attribute3;
    private int min;
    private int max;

    // 构造器、getter方法
    public String getAttribute1() { return attribute1; }
    public String getAttribute2() { return attribute2; }
    public String getAttribute3() { return attribute3; }
    public int getMin() { return min; }
    public int getMax() { return max; }
}

// 聚合结果类
class OccupancyAggregated {
    private String attribute1;
    private String attribute2;
    private String attribute3;
    private int totalMin;
    private int totalMax;

    public OccupancyAggregated(String attribute1, String attribute2, String attribute3, int totalMin, int totalMax) {
        this.attribute1 = attribute1;
        this.attribute2 = attribute2;
        this.attribute3 = attribute3;
        this.totalMin = totalMin;
        this.totalMax = totalMax;
    }

    // getter方法(按需添加)
    public int getTotalMin() { return totalMin; }
    public int getTotalMax() { return totalMax; }
}

方法1:使用Collectors.toMap直接合并(最简洁)

利用toMap的分组键、初始映射、合并函数,一步完成分组与累加:

List<OccupancyAggregated> aggregatedList = occupancyEntries.stream()
    .collect(Collectors.toMap(
        // 分组键:用三个属性组成自定义键(Java16+推荐用record,自动实现equals/hashCode)
        entry -> new GroupKey(entry.getAttribute1(), entry.getAttribute2(), entry.getAttribute3()),
        // 初始映射:将原始对象转为聚合对象,初始化求和字段
        entry -> new OccupancyAggregated(
            entry.getAttribute1(),
            entry.getAttribute2(),
            entry.getAttribute3(),
            entry.getMin(),
            entry.getMax()
        ),
        // 合并函数:同一分组下累加min和max
        (agg1, agg2) -> new OccupancyAggregated(
            agg1.getAttribute1(),
            agg1.getAttribute2(),
            agg1.getAttribute3(),
            agg1.getTotalMin() + agg2.getTotalMin(),
            agg1.getTotalMax() + agg2.getTotalMax()
        )
    ))
    // 将Map中的聚合值转为列表
    .values().stream()
    .collect(Collectors.toList());

分组键实现(Java16+用record)

// Java16+ 自动生成正确的equals和hashCode,无需手动实现
record GroupKey(String attribute1, String attribute2, String attribute3) {}

分组键实现(Java8)

如果用Java8,需手动实现equals和hashCode:

class GroupKey {
    private final String attribute1;
    private final String attribute2;
    private final String attribute3;

    public GroupKey(String attribute1, String attribute2, String attribute3) {
        this.attribute1 = attribute1;
        this.attribute2 = attribute2;
        this.attribute3 = attribute3;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        GroupKey groupKey = (GroupKey) o;
        return Objects.equals(attribute1, groupKey.attribute1) &&
               Objects.equals(attribute2, groupKey.attribute2) &&
               Objects.equals(attribute3, groupKey.attribute3);
    }

    @Override
    public int hashCode() {
        return Objects.hash(attribute1, attribute2, attribute3);
    }

    // getter方法
    public String getAttribute1() { return attribute1; }
    public String getAttribute2() { return attribute2; }
    public String getAttribute3() { return attribute3; }
}

方法2:使用groupingBy+reducing

先按分组键分组,再对每组元素归约累加,最后转换为聚合对象:

List<OccupancyAggregated> aggregatedList = occupancyEntries.stream()
    .collect(Collectors.groupingBy(
        entry -> new GroupKey(entry.getAttribute1(), entry.getAttribute2(), entry.getAttribute3()),
        // 归约逻辑:合并两个原始对象,累加min和max
        Collectors.reducing(
            new OccupancyEntry("", "", "", 0, 0), // 初始空对象
            (entry1, entry2) -> new OccupancyEntry(
                entry1.getAttribute1(), // 同一分组属性一致,用任意一个即可
                entry1.getAttribute2(),
                entry1.getAttribute3(),
                entry1.getMin() + entry2.getMin(),
                entry1.getMax() + entry2.getMax()
            )
        )
    ))
    // 将分组结果转为聚合对象列表
    .entrySet().stream()
    .map(entry -> {
        GroupKey key = entry.getKey();
        OccupancyEntry reducedEntry = entry.getValue();
        return new OccupancyAggregated(
            key.getAttribute1(),
            key.getAttribute2(),
            key.getAttribute3(),
            reducedEntry.getMin(),
            reducedEntry.getMax()
        );
    })
    .collect(Collectors.toList());

关键优化点

  1. 避免多次流处理:上述两种方法均通过一次Stream操作完成所有逻辑,无需拆分步骤,提升代码简洁性与执行效率。
  2. 分组键正确性:必须保证分组键的equals和hashCode方法正确实现,否则分组会失效(Java16+的record可自动解决此问题)。
  3. 归约逻辑简化:如果允许给OccupancyAggregated添加合并方法,可进一步简化代码:
// 在OccupancyAggregated中添加合并方法
public OccupancyAggregated merge(OccupancyAggregated other) {
    this.totalMin += other.totalMin;
    this.totalMax += other.totalMax;
    return this;
}

// 此时toMap的合并函数可简化为方法引用
(agg1, agg2) -> agg1.merge(agg2)

内容的提问来源于stack exchange,提问作者Bismal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:55:21