How to groupBy object properties and map to another object using Java 8 Streams?

后端 未结 4 1454
梦如初夏
梦如初夏 2021-02-19 01:02

Suppose I have a group of bumper cars, which have a size, a color and an identifier (\"car code\") on their sides.

class BumperCar {
    int size;
    String col         


        
相关标签:
4条回答
  • 2021-02-19 01:39

    If we assume that DistGroup has hashCode/equals based on size and color, you could do it like this:

    bumperCars
        .stream()
        .map(x -> {
            List<String> list = new ArrayList<>();
            list.add(x.getCarCode());
            return new SimpleEntry<>(x, list);
        })
        .map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
        .collect(Collectors.toMap(
            Function.identity(),
            Function.identity(),
            (left, right) -> {
                left.getCarCodes().addAll(right.getCarCodes());
                return left;
            }))
        .values(); // Collection<DistGroup>
    
    0 讨论(0)
  • 2021-02-19 01:50

    Solution-1

    Just merging the two steps into one:

    List<DistGroup> distGroups = bumperCars.stream()
            .collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
            .entrySet().stream()
            .map(t -> {
                DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
                d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
                return d;
            })
            .collect(Collectors.toList());
    

    Solution-2

    Your intermediate variable would be much better if you could use groupingBy twice using both the attributes and map the values as List of codes, something like:

    Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
            .collect(Collectors.groupingBy(BumperCar::getSize,
                    Collectors.groupingBy(BumperCar::getColor,
                            Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
    

    and simply use forEach to add to the final list as:

    List<DistGroup> distGroups = new ArrayList<>();
    sizeGroupedData.forEach((size, colorGrouped) ->
            colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
    

    Note: I've updated your constructor such that it accepts the card codes list.

    DistGroup(int size, String color, List<String> carCodes) {
        this.size = size;
        this.color = color;
        addCarCodes(carCodes);
    }
    

    Further combining the second solution into one complete statement(though I would myself favor the forEach honestly):

    List<DistGroup> distGroups = bumperCars.stream()
            .collect(Collectors.groupingBy(BumperCar::getSize,
                    Collectors.groupingBy(BumperCar::getColor,
                            Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
            .entrySet()
            .stream()
            .flatMap(a -> a.getValue().entrySet()
                    .stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
            .collect(Collectors.toList());
    
    0 讨论(0)
  • 2021-02-19 01:50

    Check out my library AbacusUtil:

    StreamEx.of(bumperCars)
             .groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
             .map(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue())
             .toList();
    
    0 讨论(0)
  • 2021-02-19 02:03

    You can collect by by using BiConsumer that take (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) as parameters

    Collection<DistGroup> values = bumperCars.stream()
            .collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
                    SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
                    DistGroup distGroup = res.get(dg);
                    if(distGroup != null) {
                        distGroup.addCarCode(bc.carCode);
                    }else {
                        List<String> codes = new ArrayList();
                        distGroup = new DistGroup(bc.size, bc.color, codes);
                        res.put(dg, distGroup);
                    }
                    },HashMap::putAll).values();
    
    0 讨论(0)
提交回复
热议问题