Reputation: 33
I'm trying to add objects from the List<Farm>
to Map<Animal, List<Farm>>
public class Farm {
private String farmName;
private EnumSet<Animal> animals = EnumSet.noneOf(Animal.class);
/* ... */
}
Farm f1 = new Farm("Farm A", EnumSet.of(Animal.CAT, Animal.DOG, Animal.DUCK));
Farm f2 = new Farm("Farm B", EnumSet.of(Animal.PIG, Animal.CAT, Animal.HORSE));
Farm f3 = new Farm("Farm C", EnumSet.of(Animal.DUCK));
Task 1: add Objects to List<Farm>
List<Farm> list = new ArrayList<>();
list.add(f1);
list.add(f2);
list.add(f3);
Task 2: Add the objects from list to a map (Key: Animal
, Value: List <Farm>
) I did this task in this way:
Map<Animal, List<Farm>> map = new HashMap<>();
for(Farm farm: list) {
for(Animal an: farm.getAnimals()) {
if(!map.containsKey(an)) {
List<Farm> new_list = new ArrayList<>();
new_list.add(farm);
map.put(an, new_list);
}else {
List<Farm> old_list = map.get(an);
if(!old_list.contains(farm)) {
old_list.add(farm);
}
}
}
}
Is there a second / more efficient solution? Something like this:
Map<Animal, List<Farm>> map = list.stream().collect(Collectors.groupingBy(Farm::getAnimals)));
This does not work because getAnimals
returns an EnumSet<Animal>
.
Upvotes: 2
Views: 265
Reputation: 2821
I started from the opposite side, but I suppose it's helpful
Map<Animal, List<Farm>> map = Arrays.stream(Animal.values())
.collect(Collectors.toMap(an -> an, an -> list.stream().filter(f -> f.getAnimals().contains(an)).collect(Collectors.toList())));
There could be empty sets for an animal in the case when none of the farms contains it, but that could be easily filtered
Upvotes: 0
Reputation: 298123
You probably want to stay with the loop, but modernize it:
Map<Animal, List<Farm>> map = new EnumMap<>(Animal.class);
for(Farm farm: list)
for(Animal an: farm.getAnimals())
map.computeIfAbsent(an, x -> new ArrayList<>()).add(farm);
In your loop, add(farm)
redundantly appeared in both branches, as you always add it to the List
. Then, computeIfAbsent
allows to eliminate the conditional, as it will return an existing value or construct a new value, put and return it. The groupingBy
collector also uses this method internally.
Using a Stream operation for the same has the disadvantage that you need a temporary holder for two values, e.g.
Map<Animal, List<Farm>> map = list.stream()
.flatMap(farm -> farm.getAnimals().stream()
.map(animal -> new AbstractMap.SimpleImmutableEntry<>(animal, farm)))
.collect(Collectors.groupingBy(Map.Entry::getKey,
() -> new EnumMap<>(Animal.class),
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
Upvotes: 3