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

Java 8 Stream如何按类别、颜色、城市多属性分组列表?

Java Stream 实现多属性分组

需求说明

拥有一个Items列表,希望按照Category、Color、City三个属性对该列表进行分组。

示例数据

ID;Category;Color;City;Name
1;1;Red;Rome;Shoe A
2;1;Red;Paris;Shoe C
3;1;Green;Rome;Scarf
4;1;Red;Rome;Shoe B
5;2;Red;Rome;Scarf
6;1;Red;Rome;Scarf
7;1;Green;Rome;Shoe

期望分组结果

1;Red;Rome => ID (1,4,6)
1;Red;Paris => ID(2)
1;Green;Rome => ID(3,7)
2;Red;Rome => ID(5)

已掌握的单属性分组代码

Map<Category, List<Items>> _items = items.stream()
    .collect(Collectors.groupingBy(item -> item.getCategory()));

多属性分组实现方案

要实现多属性分组,核心是定义一个能唯一标识分组维度的键对象,确保该对象的equals()和hashCode()方法能正确判断分组是否相同。下面提供几种常用实现方式:

方式1:自定义分组键类

自行编写包含目标分组属性的类,重写equals()和hashCode()方法保证分组逻辑正确:

import java.util.Objects;

// 自定义分组键类
class GroupKey {
    private final Integer category;
    private final String color;
    private final String city;

    public GroupKey(Integer category, String color, String city) {
        this.category = category;
        this.color = color;
        this.city = city;
    }

    @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(category, groupKey.category) &&
               Objects.equals(color, groupKey.color) &&
               Objects.equals(city, groupKey.city);
    }

    @Override
    public int hashCode() {
        return Objects.hash(category, color, city);
    }

    // 重写toString,直接输出期望的键格式
    @Override
    public String toString() {
        return category + ";" + color + ";" + city;
    }
}

基于该类完成分组,并收集对应ID列表:

Map<GroupKey, List<Integer>> groupedById = items.stream()
    .collect(Collectors.groupingBy(
        item -> new GroupKey(item.getCategory(), item.getColor(), item.getCity()),
        Collectors.mapping(Item::getId, Collectors.toList())
    ));

// 输出符合要求的结果格式
groupedById.forEach((key, ids) -> {
    System.out.println(key + " => ID (" + String.join(",", ids.stream().map(String::valueOf).toList()) + ")");
});

方式2:使用Java 16+ Record(推荐)

Java 16引入的Record会自动生成equals()、hashCode()和toString()方法,代码更简洁:

// 用Record定义分组键,自动实现核心方法
record GroupKey(Integer category, String color, String city) {}

分组逻辑与方式1一致,仅键的创建更简洁:

Map<GroupKey, List<Integer>> groupedById = items.stream()
    .collect(Collectors.groupingBy(
        item -> new GroupKey(item.getCategory(), item.getColor(), item.getCity()),
        Collectors.mapping(Item::getId, Collectors.toList())
    ));

// 输出时拼接成期望的键格式
groupedById.forEach((key, ids) -> {
    String keyStr = key.category() + ";" + key.color() + ";" + key.city();
    System.out.println(keyStr + " => ID (" + String.join(",", ids.stream().map(String::valueOf).toList()) + ")");
});

方式3:字符串拼接作为键(简易但有风险)

如果属性值不会包含分隔符(如;),可以直接拼接属性值作为分组键,这种方式最简便但容错性差:

Map<String, List<Integer>> groupedById = items.stream()
    .collect(Collectors.groupingBy(
        item -> item.getCategory() + ";" + item.getColor() + ";" + item.getCity(),
        Collectors.mapping(Item::getId, Collectors.toList())
    ));

// 直接输出结果
groupedById.forEach((key, ids) -> {
    System.out.println(key + " => ID (" + String.join(",", ids.stream().map(String::valueOf).toList()) + ")");
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 02:57:31