【问题标题】:Java 8 Stream "collect and group by" objects that map to multiple keys [duplicate]Java 8 Stream“收集和分组”映射到多个键的对象[重复]
【发布时间】:2015-10-12 17:11:52
【问题描述】:

我有以下物品:

public class Item {
    String value;
    List<Person> owners;
    Person creator;
}

public class Person {
    String name;
    int id;
    Person manager;
}

现在我有一个包含 3 个 Item 对象的列表:

i1 -> {value="1", owners=[p1, p2, p3], creator=p4}
i2 -> {value="2", owners=[p2, p3], creator=p5}
i3 -> {value="3", owners=[p5], creator=p1}

Person 对象如下:

p1 -> {manager=m1, ...}
p2 -> {manager=m2, ...}
p3 -> {manager=m3, ...}
p4 -> {manager=m2, ...}
p5 -> {manager=m1, ...}

我想根据所有者和创建者的管理者对 Item 对象流进行分组。结果Map&lt;Person, List&lt;Item&gt;&gt; 应如下所示:

{
  m1: [i1, i2, i3],
  m2: [i1, i2],
  m3: [i1, i2]
}

我认为使用 Stream 和 Collector API,我可以制作从 Item 到管理器的映射,首先是 Map&lt;Item, List&lt;Person&gt;&gt;,然后反向映射。但是有什么方法可以只使用 Stream 和 Collectors 来制作我想要的映射吗?

【问题讨论】:

    标签: java-8 java-stream collectors


    【解决方案1】:

    我认为,这只能通过中间“对”值来记住人员/经理与原始项目之间的关联。在 Java 的标准 API 中没有标准对类型的情况下,我们不得不求助于最接近 Pair 类型的 Map.Entry

    Map<Person, List<Item>> map = list.stream()
      .flatMap(item->item.getOwners().stream()
        .map(p->new AbstractMap.SimpleEntry<>(p.getManager(), item)))
      .collect(Collectors.groupingBy(Map.Entry::getKey,
        Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
    

    使用import static 改进后,我们得到

    Map<Person, List<Item>> map = list.stream()
      .flatMap(item->item.getOwners().stream().map(p->new SimpleEntry<>(p.getManager(), item)))
      .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())));
    

    结果

    m1: [i1,i3]
    m3: [i1,i2]
    m2: [i1,i2]
    

    这有所不同,因为首先,标准地图没有定义的顺序,其次,我认为您在期望方面犯了一个错误,因为您的示例数据中 m1i2 没有关联。

    【讨论】:

    • 我还需要项目列表的创建者的映射。 m1 -> p5 -> i2 的创建者。但我想我可以稍微修改一下你的解决方案并做 .map(item -> item.getCreator()...) 然后合并对应的 List 为同一个 Person 键(经理)。
    • 其实我还想要的是基于映射的多个属性。这里回答stackoverflow.com/questions/28508253/…
    【解决方案2】:

    我的免费 StreamEx 库很好地支持这些场景,它增强了标准 Stream API:

    Map<Person, List<Item>> map = StreamEx.of(list) // create an enhanced stream of Item
                    // create a stream of Entry<Item, manager>
                    .cross(item -> item.getOwners().stream().map(Person::getManager))
                    // swap keys and values to get stream of Entry<manager, Item>
                    .invert()
                    .grouping();
    

    在内部它类似于@Holger 解决方案。

    【讨论】:

      猜你喜欢
      • 2019-04-19
      • 2021-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-26
      • 2023-03-15
      相关资源
      最近更新 更多