Java 8 Stream"收集并分组"映射到多个键的对象

der*_*dji 6 java-8 java-stream collectors

我有以下对象:

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

public class Person {
    String name;
    int id;
    Person manager;
}
Run Code Online (Sandbox Code Playgroud)

现在我有一个包含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}
Run Code Online (Sandbox Code Playgroud)

Person对象如下:

p1 -> {manager=m1, ...}
p2 -> {manager=m2, ...}
p3 -> {manager=m3, ...}
p4 -> {manager=m2, ...}
p5 -> {manager=m1, ...}
Run Code Online (Sandbox Code Playgroud)

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

{
  m1: [i1, i2, i3],
  m2: [i1, i2],
  m3: [i1, i2]
}
Run Code Online (Sandbox Code Playgroud)

我认为使用Stream和Collector API,我可以Map<Item, List<Person>>先将Item从Item创建到管理器,然后再反转映射.但有没有办法只使用Stream和Collectors进行我想要的映射?

Hol*_*ger 19

我认为,这只能通过中间的"配对"值来记住人/经理与原始项之间的关联.如果缺少Java标准API中的标准对类型,我们必须求助于Map.Entry最接近Pair类型的类型:

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())));
Run Code Online (Sandbox Code Playgroud)

使用后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())));
Run Code Online (Sandbox Code Playgroud)

它导致了

m1: [i1,i3]
m3: [i1,i2]
m2: [i1,i2]
Run Code Online (Sandbox Code Playgroud)

这是不同的,因为首先,标准地图没有定义的顺序,其次,我认为你在你的期望中犯了一个错误,因为你的示例数据m1没有关联i2.