
本文介绍如何基于 java 8 stream api 和 collectors.groupingby,按对象的指定字段(如 shape、size 或二者组合)将 list>,适用于多策略分组场景。
在实际开发中,常需根据业务类型(如 "type_1"、"type_2")对同一对象列表执行不同维度的分组逻辑:仅按 shape、仅按 size,或联合按 shape 和 size。核心目标是将原始 List> —— 即每个子列表包含字段值相同的对象。
✅ 基础分组:按单个字段(如 shape)
使用 Collectors.groupingBy(Obj::getShape) 可将对象按 shape 分组为 Map
public List> groupByShape(List
objs) { return new ArrayList<>(objs.stream() .collect(Collectors.groupingBy(Obj::getShape)) .values()); }
⚠️ 注意:请确保 Obj 类已提供 getShape()(及 getSize())等标准 getter 方法;若字段为 private,直接访问 obj.shape 在 lambda 中不可行。
✅ 进阶分组:按多个字段(如 shape + size)
联合分组需构造唯一键。推荐使用 AbstractMap.SimpleEntry 或自定义键类,但最简洁安全的方式是用 Record(Java 14+)或匿名对象(Java 17+),或拼接字符串(需确保无歧义):
// 方式1:使用 record(推荐,类型安全、可读性高) public List> groupByShapeAndSize(List
objs) { return new ArrayList<>(objs.stream() .collect(Collectors.groupingBy( obj -> new AbstractMap.SimpleImmutableEntry<>(obj.getShape(), obj.getSize()) )) .values()); } // 方式2:字符串拼接(兼容低版本,但需注意转义,如 shape="a_b", size="c_d" 可能冲突) public List > groupByShapeAndSizeSafe(List
objs) { return new ArrayList<>(objs.stream() .collect(Collectors.groupingBy( obj -> obj.getShape() + "\0" + obj.getSize() // 使用不可见分隔符 \0 更安全 )) .values()); }
✅ 统一调度:在 functionName 中集成策略
将分组逻辑封装为函数式接口,使主流程清晰可维护:
public void functionName(Map> objMap) { Map , List >>> strategies = Map.of( "type_1", this::groupByShape, "type_2", this::groupBySize, "type_3", this::groupByShapeAndSize ); objMap.forEach((type, objs) -> { Function
, List
>> strategy = strategies.get(type); if (strategy != null) { List
> grouped = strategy.apply(objs); // ✅ 此处执行后续处理:如统计、聚合、持久化等 processGroupedResults(grouped); } }); } private void processGroupedResults(List
> grouped) { for (int i = 0; i < grouped.size(); i++) { System.out.println("Group " + (i + 1) + ": " + grouped.get(i).size() + " objects"); // e.g., grouped.get(i) is a List
with same shape/size } }
? 验证示例输出
以输入:
{
"key": "type_3",
"value": [
{"size":"m1","shape":"s1","name":"Obj_1"},
{"size":"m2","shape":"s1","name":"Obj_2"},
{"size":"m1","shape":"s1","name":"Obj_3"}
]
}调用 groupByShapeAndSize() 后,将生成:
- [(s1,m1) → [Obj_1, Obj_3]]
- [(s1,m2) → [Obj_2]]
→ 最终 List- > 包含两个子列表,完全符合预期。
✅ 总结
- 使用 Collectors.groupingBy 是实现动态分组的最简高效方案;
- 单字段分组直接传方法引用(如 Obj::getShape);
- 多字段分组优先使用 SimpleImmutableEntry 或 record 构建复合键,避免字符串拼接风险;
- 将分组策略与主逻辑解耦(如 Map
),提升可扩展性与可测试性; - 所有分组结果均为内存内 List
- >,无需额外依赖,开箱即用。










