First things first, let me add the actual "example code":
Map<CarBrand, List<Car>> allCarsAndBrands = new HashMap();
final String bmwBrandName = "BMW";
final String audiBrandName = "AUDI";
List<Car> bmwCars = new ArrayList();
bmwCars.add(new Car(CarType.FAST, "Z4", "silver", bmwBrandName));
bmwCars.add(new Car(CarType.FAST, "M3", "red", bmwBrandName));
bmwCars.add(new Car(CarType.SLOW, "X1", "black", bmwBrandName));
List<Car> audiCars = new ArrayList();
audiCars.add(new Car(CarType.FAST, "S3", "yellow", audiBrandName));
audiCars.add(new Car(CarType.FAST, "R8", "silver", audiBrandName));
audiCars.add(new Car(CarType.SLOW, "A1", "white", audiBrandName));
allCarsAndBrands.put(new CarBrand(bmwBrandName), bmwCars);
allCarsAndBrands.put(new CarBrand(audiBrandName), audiCars);
Map<CarType, Map<CarBrand, List<Car>>> mappedCars;
Problem
My goal on this is to populate mappedCars
by CarType
, which would result in two big sets: one containing all FAST cars and the other all SLOW cars (or any future "types", each one having the previous map structure with CarBrand
and the related cars).
I'm currently failing to find the proper use of Collections/Streams for this "map with lists inside other map". I've had other cases with simple maps/lists but this one is proving to be trickier for me.
Attempts
Here's an initial code "attempt":
mappedCars = allCarsAndBrands.entrySet()
.stream()
.collect(
groupingBy(Car::getType,
groupingBy(Map.Entry::getKey)
)
);
I'm also getting the "non-static cannot be referenced error" (Map.Entry::getKey) but this is due the fact that I'm failing to match the actual expected return (Static context cannot access non-static in Collectors)
I'm simply confused at this point, tried using Collectors.toMap
too but still can't get a working grouping.
Extras
Here are the class definitions for this example:
class CarBrand {
CarBrand(String name) {
this.name = name;
}
String name;
}
class Car {
Car(CarType type, String name, String color, String brandName) {
this.type = type;
this.name = name;
this.color = color;
this.brandName = brandName;
}
public CarType getType() {
return type;
}
CarType type;
String name;
String color;
String brandName;
}
enum CarType {
FAST,
SLOW,
}
EDIT: "DIRTY" SOLUTION
Here's a "hackish" solution (based on the comments suggestions, will check the answers!):
Map<CarType, Map<CarBrand, List<Car>>> mappedCars = allCarsAndBrands
.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.groupingBy(
Car::getType,
Collectors.groupingBy(
car -> allCarsAndBrands.keySet().stream().filter(brand -> brand.name == car.brandName).findFirst().get(),
Collectors.toList()
)
));
As mentioned in the comments (should've added here before), there's a "business constraint" that adds some limitations for the solution. I also didn't feel like creating a new CarBrand
since in the real world that's not that simple as seen on this... but again, using the original map and filtering + find is just bad.