Java: Как добавить объекты из списка <T>на карту (ключ: перечисление, значение списка <T>) - PullRequest
2 голосов
/ 26 июня 2019

Я пытаюсь добавить объекты из List<Farm> в 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));

Задача 1: добавить объекты в List<Farm>

List<Farm> list = new ArrayList<>();
list.add(f1);
list.add(f2);
list.add(f3);

Задача 2: Добавитьобъекты из списка на карту (ключ: Animal, значение: List <Farm>) Я выполнил эту задачу следующим образом:

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

Есть ли второе / более эффективное решение?Примерно так:

Map<Animal, List<Farm>> map = list.stream().collect(Collectors.groupingBy(Farm::getAnimals)));

Это не работает, потому что getAnimals возвращает EnumSet<Animal>.

Ответы [ 2 ]

3 голосов
/ 26 июня 2019

Вы, вероятно, хотите остаться в цикле, но модернизировать его:

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

В вашем цикле add(farm) появилось избыточно в обеих ветвях, как вы всегда добавляете его в List. Затем computeIfAbsent позволяет исключить условное условие, так как оно вернет существующее значение или создаст новое значение, поместит и вернет его. Коллектор groupingBy также использует этот метод внутри.

Использование операции Stream для одного и того же недостатка заключается в том, что вам необходим временный держатель для двух значений, например,

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())));
0 голосов
/ 26 июня 2019

Я начал с противоположной стороны, но я полагаю, это полезно

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

Могут быть пустые наборы для животного в случае, если ни одна из ферм не содержит его, но это может быть легко отфильтровано

...