java8 list分组
在 Java 8 中,可以使用 Stream API 和 Collectors.groupingBy
对 List
进行分组。以下是常见的分组场景和实现方法:
1. 基础分组(按对象属性分组)
根据对象的某个字段分组(例如按 category
分组):
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Product {
private String category;
private String name;
// 构造方法、Getter/Setter 省略
}
List<Product> products = ...;
// 按 category 分组
Map<String, List<Product>> groupedByCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
2. 分组后统计(如计数、求和)
分组后对每组数据进行统计:
// 按 category 分组,统计每组的数量
Map<String, Long> countByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.counting()
));
// 按 category 分组,计算每组的总价(假设有 getPrice() 方法)
Map<String, Double> sumByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.summingDouble(Product::getPrice)
));
3. 复杂条件分组
使用 Lambda 表达式自定义分组逻辑:
// 按价格范围分组(如 <100, 100-500, >500)
Map<String, List<Product>> groupByPriceRange = products.stream()
.collect(Collectors.groupingBy(p -> {
if (p.getPrice() < 100) return "Low";
else if (p.getPrice() <= 500) return "Medium";
else return "High";
}));
4. 多级分组(嵌套分组)
先按 category
分组,再按 subCategory
分组:
Map<String, Map<String, List<Product>>> multiGrouping = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.groupingBy(Product::getSubCategory)
));
5. 分组后对值进行处理
分组后对值进行转换(如提取名称):
// 按 category 分组,提取每组的名称集合
Map<String, List<String>> namesByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.mapping(Product::getName, Collectors.toList())
));
6. 过滤后再分组
先过滤数据再分组:
// 过滤价格 > 0 的商品后按 category 分组
Map<String, List<Product>> filteredGroup = products.stream()
.filter(p -> p.getPrice() > 0)
.collect(Collectors.groupingBy(Product::getCategory));
完整示例
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class GroupingDemo {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product("Electronics", "Laptop", 1200.0),
new Product("Electronics", "Phone", 800.0),
new Product("Books", "Java Guide", 50.0)
);
// 按 category 分组
Map<String, List<Product>> byCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
// 输出结果
byCategory.forEach((category, list) -> {
System.out.println("Category: " + category);
list.forEach(p -> System.out.println(" " + p.getName()));
});
}
}
class Product {
private String category;
private String name;
private double price;
public Product(String category, String name, double price) {
this.category = category;
this.name = name;
this.price = price;
}
// Getters 省略
}
关键点
Collectors.groupingBy
:核心分组方法。- 方法引用:如
Product::getCategory
简化代码。 - 下游收集器:如
Collectors.counting()
或Collectors.toList()
处理分组后的数据。
根据具体需求选择合适的分组方式!
**
分组key 自定义
**
在 Java 8 中实现多级分组时,若需要将中文的 Key
转换为预定义的码值(如枚举、编码或英文缩写),可以通过以下步骤完成:
步骤 1:定义中文到码值的映射关系
使用 Map
或枚举类维护中文与码值的对应关系。例如:
// 方式 1:静态 Map 存储映射
private static final Map<String, String> CATEGORY_CODE_MAP = new HashMap<>();
static {
CATEGORY_CODE_MAP.put("电子产品", "ELEC");
CATEGORY_CODE_MAP.put("书籍", "BOOK");
// 更多映射...
}
// 方式 2:使用枚举
public enum CategoryCode {
ELECTRONICS("电子产品", "ELEC"),
BOOKS("书籍", "BOOK");
private final String chineseName;
private final String code;
CategoryCode(String chineseName, String code) {
this.chineseName = chineseName;
this.code = code;
}
public static String getCode(String chineseName) {
return Arrays.stream(values())
.filter(e -> e.chineseName.equals(chineseName))
.findFirst()
.map(e -> e.code)
.orElse("UNKNOWN"); // 默认值
}
}
步骤 2:在分组时转换 Key
使用 groupingBy
时,通过映射关系将中文转为码值:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Product {
private String category; // 中文分类,如 "电子产品"
private String subCategory; // 中文子分类,如 "手机"
// 其他字段和方法...
}
List<Product> products = ...;
// 多级分组(按 category -> subCategory)
Map<String, Map<String, List<Product>>> groupedProducts = products.stream()
.collect(Collectors.groupingBy(
// 第一级分组:中文 category 转码值
product -> CATEGORY_CODE_MAP.getOrDefault(
product.getCategory(),
"DEFAULT_CODE" // 处理未匹配的情况
),
// 第二级分组:中文 subCategory 转码值
Collectors.groupingBy(
product -> CategoryCode.getCode(product.getSubCategory()) // 使用枚举转换
)
));
步骤 3:处理未匹配的中文 Key
如果存在未定义的中文 Key,需设置默认值或抛出异常:
// 使用 getOrDefault 设置默认值
product -> CATEGORY_CODE_MAP.getOrDefault(product.getCategory(), "UNKNOWN")
// 或抛出明确异常
product -> {
String code = CATEGORY_CODE_MAP.get(product.getCategory());
if (code == null) {
throw new IllegalArgumentException("未找到分类编码: " + product.getCategory());
}
return code;
}
完整示例
import java.util.*;
import java.util.stream.Collectors;
public class MultiLevelGroupingWithCode {
// 定义中文到码值的映射
private static final Map<String, String> CATEGORY_CODE_MAP = new HashMap<>();
static {
CATEGORY_CODE_MAP.put("电子产品", "ELEC");
CATEGORY_CODE_MAP.put("书籍", "BOOK");
}
private static final Map<String, String> SUB_CATEGORY_CODE_MAP = new HashMap<>();
static {
SUB_CATEGORY_CODE_MAP.put("手机", "PHONE");
SUB_CATEGORY_CODE_MAP.put("笔记本电脑", "LAPTOP");
SUB_CATEGORY_CODE_MAP.put("编程书籍", "PROG_BOOK");
}
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product("电子产品", "手机"),
new Product("电子产品", "笔记本电脑"),
new Product("书籍", "编程书籍")
);
// 多级分组:中文转码值
Map<String, Map<String, List<Product>>> grouped = products.stream()
.collect(Collectors.groupingBy(
p -> CATEGORY_CODE_MAP.getOrDefault(p.getCategory(), "UNKNOWN"),
Collectors.groupingBy(
p -> SUB_CATEGORY_CODE_MAP.getOrDefault(p.getSubCategory(), "UNKNOWN")
)
));
// 输出结果
grouped.forEach((categoryCode, subMap) -> {
System.out.println("Category Code: " + categoryCode);
subMap.forEach((subCode, list) -> {
System.out.println(" Sub-Category Code: " + subCode);
list.forEach(p -> System.out.println(" " + p));
});
});
}
}
class Product {
private String category;
private String subCategory;
public Product(String category, String subCategory) {
this.category = category;
this.subCategory = subCategory;
}
// Getters...
}
输出结果
Category Code: ELEC
Sub-Category Code: PHONE
Product{category='电子产品', subCategory='手机'}
Sub-Category Code: LAPTOP
Product{category='电子产品', subCategory='笔记本电脑'}
Category Code: BOOK
Sub-Category Code: PROG_BOOK
Product{category='书籍', subCategory='编程书籍'}
优化建议
-
集中管理映射关系
将映射存储在配置文件或数据库中,提高灵活性。 -
使用工具类
封装中文到码值的转换逻辑:public class CodeConverter { public static String getCategoryCode(String chineseName) { // 实现转换逻辑 } public static String getSubCategoryCode(String chineseName) { // 实现转换逻辑 } }
-
防御性编程
确保所有可能的中文 Key 都有对应的码值,避免NullPointerException
。
通过这种方式,可以在分组时动态将中文 Key 转换为业务需要的码值!