问题
I have the following objects:
public class Item {
String value;
List<Person> owners;
Person creator;
}
public class Person {
String name;
int id;
Person manager;
}
now i have the a list containing 3 Item objects:
i1 -> {value="1", owners=[p1, p2, p3], creator=p4}
i2 -> {value="2", owners=[p2, p3], creator=p5}
i3 -> {value="3", owners=[p5], creator=p1}
the Person objects are as follows:
p1 -> {manager=m1, ...}
p2 -> {manager=m2, ...}
p3 -> {manager=m3, ...}
p4 -> {manager=m2, ...}
p5 -> {manager=m1, ...}
I want to group the stream of Item objects based on managers of the owners and creator. The result Map<Person, List<Item>>
should look like:
{
m1: [i1, i2, i3],
m2: [i1, i2],
m3: [i1, i2]
}
I think using Stream and Collector APIs, I can make the map from Item to managers, Map<Item, List<Person>>
first, then reverse the mapping. But is there any way to make the mapping I want using Stream and Collectors only?
回答1:
I think, this is only possible with an intermediate “pair” value to remember the association between a person/manager and the originating item. In absence of a standard pair type in Java’s standard API, we have to resort to Map.Entry
which comes closest to a Pair
type:
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())));
after improving it using import static
we get
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())));
It results in
m1: [i1,i3]
m3: [i1,i2]
m2: [i1,i2]
which differs because first, the standard map has no defined ordering, second, I think you made a mistake regarding you expectation as m1
is not associated with i2
in your example data.
回答2:
Such scenarios are well supported by my free StreamEx library which enhances standard 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();
Internally it's similar to @Holger solution.
来源:https://stackoverflow.com/questions/33086686/java-8-stream-collect-and-group-by-objects-that-map-to-multiple-keys