Java 收集器按动态字段分组
Java Collectors grouping-by dynamic fields
我有以下嵌套的 groupingBy 块:
Map<String,Map<String,Long>> namesCountersMap =
events.stream().collect(
Collectors.groupingBy(
namesDAO::getName,
Collectors.groupingBy(
genericDAO::SOME_DYNAMIC_FIELD,
Collectors.counting())
)
);
有一种情况我需要调用此块 3 次,我唯一更改的是内部 groupingBy 字段 ("SOME_DYNAMIC_FIELD")。
基本上我想做的是,每次都对不同的字段(在第二级)使用分组依据和计数,然后合并结果。
示例:
{
"NamesRecords": {
"Sam": {
"Cars": 4
"Bags": 6
"Houses": 2
},
"Bob": {
"Cars": 2
"Bags": 1
"Houses": 3
},
}
}
如您所见,第 2 级的 group-by 由 3 个不同的字段完成,这就是为什么我需要将此代码块复制三次。
如果我将 fieldToGroupBy 参数传递给函数,有没有办法动态使用它?或者,如果您对如何避免代码重复有任何其他想法,我很想听听。
Collectors.groupingBy
is a Function<T,R>
的第一个参数,因此您可以使用该类型的变量。
它只是一个函数式接口,带有一个方法来提取分组依据的值,它可以通过方法引用、lambda 表达式、匿名class,或任何其他类型的 class.
Function<namesDAO, String> classifier2 = genericDAO::SOME_DYNAMIC_FIELD;
Map<String,Map<String,Long>> namesCountersMap =
events.stream().collect(
Collectors.groupingBy(
namesDAO::getName,
Collectors.groupingBy(
classifier2,
Collectors.counting())
)
);
现在您可以为 classifier2
分配不同的值,例如
Function<namesDAO, String> classifier2 = someDAO::getCars;
Function<namesDAO, String> classifier2 = otherDAO::getBags;
Function<namesDAO, String> classifier2 = dao -> dao.getHouses();
public class CarDetailsService {
private final CarRepository carRepository;
private final Map<String, Function<CarDTO, String>> carColumnMapper = new HashMap<>();
public ApplicationDetailsServiceImpl(CarRepository carRepository) {
this.carRepository = carRepository;
//---- Initialise all the mappings ------- //
carColumnMapper.put("BRAND", CarDTO::getBrandName);
carColumnMapper.put("MILEAGE", CarDTO::getMileage);
}
public Map<String, List<CarDTO>> getListOfCars(String groupBy) {
return carRepository.findAll()
.stream()
.map(toCarDTO)
.collect(groupingBy(carColumnMapper.get(groupBy.toUpperCase())));
}
Function<CarDetails, CarDTO> toCarDTO = (carDetails) -> CarDTO
.builder()
.brand(carDetails.getBrand())
.engineCapacity(carDetails.getEngineCapacity())
.mileage(carDetails.getMileage())
.fuel(carDetails.getFuel())
.price(carDetails.getPrice())
.build();
}
我有以下嵌套的 groupingBy 块:
Map<String,Map<String,Long>> namesCountersMap =
events.stream().collect(
Collectors.groupingBy(
namesDAO::getName,
Collectors.groupingBy(
genericDAO::SOME_DYNAMIC_FIELD,
Collectors.counting())
)
);
有一种情况我需要调用此块 3 次,我唯一更改的是内部 groupingBy 字段 ("SOME_DYNAMIC_FIELD")。
基本上我想做的是,每次都对不同的字段(在第二级)使用分组依据和计数,然后合并结果。
示例:
{
"NamesRecords": {
"Sam": {
"Cars": 4
"Bags": 6
"Houses": 2
},
"Bob": {
"Cars": 2
"Bags": 1
"Houses": 3
},
}
}
如您所见,第 2 级的 group-by 由 3 个不同的字段完成,这就是为什么我需要将此代码块复制三次。
如果我将 fieldToGroupBy 参数传递给函数,有没有办法动态使用它?或者,如果您对如何避免代码重复有任何其他想法,我很想听听。
Collectors.groupingBy
is a Function<T,R>
的第一个参数,因此您可以使用该类型的变量。
它只是一个函数式接口,带有一个方法来提取分组依据的值,它可以通过方法引用、lambda 表达式、匿名class,或任何其他类型的 class.
Function<namesDAO, String> classifier2 = genericDAO::SOME_DYNAMIC_FIELD;
Map<String,Map<String,Long>> namesCountersMap =
events.stream().collect(
Collectors.groupingBy(
namesDAO::getName,
Collectors.groupingBy(
classifier2,
Collectors.counting())
)
);
现在您可以为 classifier2
分配不同的值,例如
Function<namesDAO, String> classifier2 = someDAO::getCars;
Function<namesDAO, String> classifier2 = otherDAO::getBags;
Function<namesDAO, String> classifier2 = dao -> dao.getHouses();
public class CarDetailsService {
private final CarRepository carRepository;
private final Map<String, Function<CarDTO, String>> carColumnMapper = new HashMap<>();
public ApplicationDetailsServiceImpl(CarRepository carRepository) {
this.carRepository = carRepository;
//---- Initialise all the mappings ------- //
carColumnMapper.put("BRAND", CarDTO::getBrandName);
carColumnMapper.put("MILEAGE", CarDTO::getMileage);
}
public Map<String, List<CarDTO>> getListOfCars(String groupBy) {
return carRepository.findAll()
.stream()
.map(toCarDTO)
.collect(groupingBy(carColumnMapper.get(groupBy.toUpperCase())));
}
Function<CarDetails, CarDTO> toCarDTO = (carDetails) -> CarDTO
.builder()
.brand(carDetails.getBrand())
.engineCapacity(carDetails.getEngineCapacity())
.mileage(carDetails.getMileage())
.fuel(carDetails.getFuel())
.price(carDetails.getPrice())
.build();
}