У меня есть класс Food:
public class Food {
private Collection<FoodToFoodGroup> foodToFoodGroups;
//getters and setters
}
А вот FoodToFoodGroup выглядит так:
public class FoodToFoodGroup {
private Food food;
private FoodGroup foodGroup;
//getters and setters
}
Наконец, у каждого FoodGroup есть имя:
public class FoodGroup {
private String name;
//getters and setters
}
Все эти классы являются объектами, отображаемыми через спящий режим. Я получаю коллекцию всех сущностей Food в базе данных с помощью вызова foodRepository.findAll(), и я пытаюсь сгруппировать все экземпляры Food по имени их FoodGroup, чтобы в итоге получить Map<String, Collection<Food>>. Я пытался сделать это через Stream API, но не могу придумать способ сделать это с помощью сборщика Collectors.groupingBy.
Map<String, Collection<Food>> foodsByFoodGroupName =
foodRepository.findAll().stream()
.collect(Collectors.groupingBy(
//what goes here?
));
Обратите внимание, что Food может быть в нескольких FoodGroup, поэтому мне понадобятся любые Food, которые находятся в нескольких FoodGroup, чтобы они отображались в нескольких Collection в результирующем Map. Возможно ли это с помощью Stream API и Collectors.groupingBy()?
Пример
Если у меня есть следующие продукты:
Food: Pimento Cheese
- FoodToFoodGroups: [
- FoodGroup: cheese,
- FoodGroup: dairy
]
Food: Chocolate Milk
- FoodToFoodGroups: [
- FoodGroup: milk,
- FoodGroup: dairy
]
Я бы хотел, чтобы получившийся Map выглядел так:
Key: cheese
- Collection<Food>: [Pimento Cheese]
Key: milk
- Collection<Food>: [Chocolate Milk]
Key: dairy
- Collection<Food>: [Pimento Cheese, Chocolate Milk]
С гибернацией должен быть более простой способ поддерживать связь между Food и FoodGroup. Похоже на @ManyToMany
@MaxVollmer Я добавил пример, чтобы помочь понять
Я думаю, что в вашей модели данных есть ненужные циклические ссылки. Каждый Food может просто содержать коллекцию FoodGroups; объект FoodToFoodGroup кажется ненужным. Похоже, вы пытаетесь представить нормализованную таблицу реляционной базы данных непосредственно как объект, вместо того, чтобы объединять данные в структурированном виде. Хотя это на самом деле не влияет на решение проблемы, я думаю, что это делает семантически более трудным обсуждение.
Но проблема здесь в том, что у экземпляра Food может быть несколько экземпляров FoodGroup. Итак, как вы собираетесь сгруппировать такую штуку?
Ваш пример, похоже, не соответствует классам, которые вы дали. Читая ваш пример, я ожидал, что будет класс Food с набором FoodGroup, а не с набором FoodToFoodGroup.
@MaxVollmer Я обновил пример, чтобы точнее отразить структуру класса
Теперь в вашем примере FoodToFoodGroup содержат ТолькоFoodGroup, но в вашем коде они содержат Food и FoodGroup. Из контекста я могу предположить, что каждый FoodToFoodGroup в коллекции Food указывает на тот же Food (в основном круговая ссылка), но это не ясно в вашем коде. Там ничего не ограничивает что-то вроде FoodA -> FoodToFoodGroup -> FoodB. Также все еще не ясно, Зачем у вас есть коллекция FoodToFoodGroup, когда Food всегда является родительским. Достаточно коллекции FoodGroup.




это то, что ты ищешь?
foodRepository.findAll().stream()
.map(Food::getFoodToFoodGroup)
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(
f -> f.getFoodGroup().getName(),
HashMap::new,
Collectors.mapping(FoodToFoodGroup::getFood, Collectors.toList())));
Идеально! Collectors.mapping - именно то, что я искал.
Я не думаю, что groupingBy - это то, что вам здесь нужно, поскольку он не предназначен для размещения одного и того же элемента в нескольких группах.
Для не чисто функционального решения с использованием потоков вы можете плоско сопоставить каждый Food с набором записей foodGroupToFood, а затем добавить все пары на карту с помощью BiConsumer.
BiConsumer<Map<FoodGroup, Set<Food>>, Map.Entry<FoodGroup, Food>> addEntryToMapOfSets = (map, entry) -> {
if (map.containsKey(entry.key()))
map.get(entry.key()).add(entry.value());
else
foodGroupToFoods.put(entry.key(), Stream.of(entry.value()).collect(Collectors.toSet()))
};
Map<String, Collection<Food>> foodGroupToFoods = new HashMap<>();
foods.stream()
.flatMap(food -> food.getFoodToFoodGroup().stream()
.collect(Collectors.toMap(pair -> pair.foodGroup, pair -> pair.food))
.entrySet())
.forEach(entry -> addEntryToMapofSets(foodGroupToFoods, entry));
Отказ от ответственности: я давно не писал на Java, пожалуйста, исправьте мое использование Streams API, если это требует исправления. Обновлено: это, вероятно, сложнее, чем должно быть.
Возможно, я неправильно понимаю, но когда у
Foodесть коллекцияFoodToFoodGroup, кажется, что у каждогоFoodесть несколькоFoodGroup, так как же можно "сгруппировать все экземпляры Food по имени их FoodGroup"? КакойFoodGroupв коллекции экземпляраFoodбудет считаться их?