Java 8 个 Stream "collect and group by" 个映射到多个键的对象
Java 8 Stream "collect and group by" objects that map to multiple keys
我有以下对象:
public class Item {
String value;
List<Person> owners;
Person creator;
}
public class Person {
String name;
int id;
Person manager;
}
现在我有一个包含 3 个项目对象的列表:
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 到 managers 进行映射,Map<Item, List<Person>>
,然后反向映射。但是有什么方法可以只使用 Stream 和 Collectors 来制作我想要的映射吗?
我认为,只有使用中间“对”值才能记住 person/manager 与原始项目之间的关联。 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]
不同之处在于,首先,标准地图没有定义顺序,其次,我认为您在期望方面犯了错误,因为 m1
与示例数据中的 i2
没有关联。
我的免费 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 解决方案。
我有以下对象:
public class Item {
String value;
List<Person> owners;
Person creator;
}
public class Person {
String name;
int id;
Person manager;
}
现在我有一个包含 3 个项目对象的列表:
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 到 managers 进行映射,Map<Item, List<Person>>
,然后反向映射。但是有什么方法可以只使用 Stream 和 Collectors 来制作我想要的映射吗?
我认为,只有使用中间“对”值才能记住 person/manager 与原始项目之间的关联。 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]
不同之处在于,首先,标准地图没有定义顺序,其次,我认为您在期望方面犯了错误,因为 m1
与示例数据中的 i2
没有关联。
我的免费 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 解决方案。