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();
}