Java流分组方式,其中键是子列表中的元素

Java流分组方式,其中键是子列表中的元素,java,java-stream,Java,Java Stream,我有一门课叫《食物》: public class Food { private Collection<FoodToFoodGroup> foodToFoodGroups; //getters and setters } 最后,每个FoodGroup都有一个名称: public class FoodGroup { private String name; //getters and setters } 这些类都是通过hibernate映射的实体。通过

我有一门课叫《食物》:

public class Food {
    private Collection<FoodToFoodGroup> foodToFoodGroups;
    //getters and setters
}
最后,每个
FoodGroup
都有一个名称:

public class FoodGroup {
    private String name;
    //getters and setters
}
这些类都是通过hibernate映射的实体。通过调用
foodRepository.findAll()
,我获得了数据库中所有
Food
实体的集合,我试图根据它们的
FoodGroup
名称对所有
Food
实例进行分组,以便最终得到一个
映射。我试图通过流API来实现这一点,但无法想出使用
收集器.groupingBy
收集器来实现这一点的方法

Map<String, Collection<Food>> foodsByFoodGroupName = 
    foodRepository.findAll().stream()
        .collect(Collectors.groupingBy(
            //what goes here?
        ));
我希望生成的
Map
如下所示:

public class FoodToFoodGroup {
    private Food food;
    private FoodGroup foodGroup;
    //getters and setters
}
Key: cheese
    - Collection<Food>: [Pimento Cheese]
Key: milk
    - Collection<Food>: [Chocolate Milk]
Key: dairy
    - Collection<Food>: [Pimento Cheese, Chocolate Milk]
键:奶酪
-收藏品:[青椒奶酪]
关键词:牛奶
-收藏品:[巧克力牛奶]
关键词:乳制品
-收藏品:[青椒奶酪、巧克力牛奶]

这就是你要找的吗

foodRepository.findAll().stream()
     .map(Food::getFoodToFoodGroup)
     .flatMap(Collection::stream)
     .collect(Collectors.groupingBy(
         f -> f.getFoodGroup().getName(),
         HashMap::new,
         Collectors.mapping(FoodToFoodGroup::getFood, Collectors.toList())));

我不认为
groupingBy
是您在这里想要的,因为它不是为将同一项目放入多个组而设计的

对于使用流的非纯功能性解决方案,您可以将每个
食品
平面映射到
foodGroupToFood
条目集,然后使用
双消费者
将所有对添加到映射中

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));
BiConsumer addEntryToMapOfSets=(映射,条目)->{
if(map.containsKey(entry.key()))
map.get(entry.key()).add(entry.value());
其他的
foodGroupToFoods.put(entry.key()、Stream.of(entry.value()).collect(Collectors.toSet())
};
Map foodGroupToFoods=newhashmap();
食物
.flatMap(food->food.getFoodToFoodGroup().stream())
.collect(Collectors.toMap(pair->pair.foodGroup,pair->pair.food))
.entrySet())
.forEach(条目->addEntryToMapofSets(foodGroupToFoods,条目));
免责声明:我已经有一段时间没有用Java编写了,如果需要更正,请更正我对Streams API的使用。
编辑:这可能比它需要的更复杂。

我可能会误解,但是当
食品
有一个
FoodToFoodGroup
s集合时,每个
食品
似乎都有多个
FoodGroup
s,那么如何“根据其食品组的名称对所有食品实例进行分组”?在
Food
实例的集合中,哪个
FoodGroup
会被认为是他们的?使用hibernate,应该更容易维护
Food
FoodGroup
之间的关系。似乎
@manytomy
我认为您的数据模型有不必要的循环引用。每个
食物
可以只包含一组
食物组
FoodToFoodGroup
对象似乎没有必要。看起来您可能试图将规范化的关系数据库表直接表示为对象,而不是以结构化方式连接数据。虽然这并不会真正影响问题的解决方案,但我认为这在语义上更难讨论。您的示例似乎与您给出的类不一致。阅读您的示例,我希望类
Food
包含
FoodGroup
s的集合,而不是
FoodToFoodGroup
s的集合。现在在您的示例中,
FoodToFoodGroup
s只包含
FoodGroup
s,但在您的代码中它们包含
FoodGroup
FoodGroup
s。根据上下文,我可以假设
食品
集合中的每个
FoodToFoodGroup
都指向相同的
食品
(基本上是一个循环引用),但这在您的代码中并不清楚。没有任何东西像
FoodA
->
FoodToFoodGroup
->
FoodB
这样限制。另外,当
食物
始终是父对象时,为什么会有一个
FoodToFoodGroup
s集合还不清楚。一组
FoodGroup
s就足够了。太好了!那
收集器。映射
正是我想要的。
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));