7

我有以下对象:

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<Person, List<Item>>应如下所示:

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

我认为使用 Stream 和 Collector API,我可以先制作从 Item 到管理器Map<Item, List<Person>>的映射,然后再反向映射。但是有没有办法只使用 Stream 和 Collectors 来制作我想要的映射?

4

2 回答 2

21

我认为,只有使用中间“对”值才能记住人/经理与原始项目之间的关联。如果 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())));

改进后使用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您的示例数据无关。

于 2015-10-12T18:06:09.713 回答
4

我的免费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 解决方案。

于 2015-10-12T21:49:54.707 回答