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

MC Emperor :

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 color;
    String carCode;
}

Now I need to map the bumper cars to a List of DistGroup objects, which each contains the properties size, color and a List of car codes.

class DistGroup {
    int size;
    Color color;
    List<String> carCodes;

    void addCarCodes(List<String> carCodes) {
        this.carCodes.addAll(carCodes);
    }
}

For example,

[
    BumperCar(size=3, color=yellow, carCode=Q4M),
    BumperCar(size=3, color=yellow, carCode=T5A),
    BumperCar(size=3, color=red, carCode=6NR)
]

should result in:

[
    DistGroup(size=3, color=yellow, carCodes=[ Q4M, T5A ]),
    DistGroup(size=3, color=red, carCodes=[ 6NR ])
]

I tried the following, which actually does what I want it to do. But the problem is that it materializes the immediate result (into a Map) and I also think that it can be done at once (perhaps using mapping or collectingAndThen or reducing or something), resulting in more elegant code.

List<BumperCar> bumperCars = ...
Map<SizeColorCombination, List<BumperCar>> map = bumperCars.stream()
    .collect(groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())));

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

How can I get the desired result without using a variable for an immediate result?

Edit: How can I get the desired result without materializing the immediate result? I am merely looking for a way which does not materialize the immediate result, at least not on the surface. That means that I prefer not to use something like this:

something.stream()
    .collect(...) // Materializing
    .stream()
    .collect(...); // Materializing second time

Of course, if this is possible.


Note that I omitted getters and constructors for brevity. You may also assume that equals and hashCode methods are properly implemented. Also note that I'm using the SizeColorCombination which I use as group-by key. This class obviously contains the properties size and color. Classes like Tuple or Pair or any other class representing a combination of two arbitrary values may also be used.
Edit: Also note that an ol' skool for loop can be used instead of course, but that is not in the scope of this question.

Eugene :

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>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=35897&siteId=1