Reputation: 827
I have class Element with a list, my intended output is like this:
Map<String , List<Element>>
{
1 = [Element3, Element1],
2 = [Element2, Element1],
3 = [Element2, Element1], 4=[Element2]
}
And my input is set of element objects, I used forEach
to get the desired outcome, but I'm looking for how to collect it using collectors.toMap
. Any inputs are much appreciated
Set<Element> changes = new HashSet();
List<String> interesetList = new ArrayList();
interesetList.add("1");
interesetList.add("2");
interesetList.add("3");
Element element = new Element(interesetList);
changes.add(element);
interesetList = new ArrayList();
interesetList.add("2");
interesetList.add("3");
interesetList.add("4");
element = new Element(interesetList);
changes.add(element);
Map<String, List<Element>> collect2 = new HashMap();
changes.forEach(element -> {
element.getInterestedList().forEach(tracker -> {
collect2.compute(tracker, ( key , val) -> {
List<Element> elementList = val == null ? new ArrayList<Element>() : val;
elementList.add(Element);
return elementList;
});
});
});
class Element {
List<String> interestedList;
static AtomicInteger sequencer = new AtomicInteger(0);
String mName;
public Element(List<String> aList) {
interestedList = aList;
mName = "Element" + sequencer.incrementAndGet();
}
public List<String> getInterestedList() {
return interestedList;
}
@Override
public String toString() {
return mName;
}
}
Upvotes: 3
Views: 1880
Reputation: 34450
You can do it by using Collectors.groupingBy
instead of Collectors.toMap
, along with Collectors.mapping
, which adapts a collector to another collector:
Map<String, List<Element>> result = changes.stream()
.flatMap(e -> e.getInterestedList().stream().map(t -> Map.entry(t, e)))
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
You need to use the Stream.flatMap
method first and then pair the elements of the inner lists with the current Element
instance. I did this via the new Java 9's Map.entry(key, value)
method. If you're not on Java 9 yet, you could change it to new AbstractMap.SimpleEntry<>(key, value)
.
After flatmapping, we need to collect instances of Map.Entry
. So I'm using Collectors.groupingBy
to classify entries by key (where we had previously stored each element of the inner lists, aka what you call tracker
in your code). Then, as we don't want to have instances of List<Map.Entry<String, Element>>
as the values of the map, we need to transform each Map.Entry<String, Element>
of the stream to just Element
(that's why I'm using Map.Entry::getValue
as the first argument of Collectors.mapping
). We also need to specify a downstream collector (here Collectors.toList()
), so that the outer Collectors.groupingBy
collector knows where to place all the adapted elements of the stream that belong to each group.
A shorter and surely more efficient way to do the same (similar to your attempt) could be:
Map<String, List<Element>> result = new HashMap<>();
changes.forEach(e ->
e.getInterestedList().forEach(t ->
result.computeIfAbsent(t, k -> new ArrayList<>()).add(e)));
This uses Map.computeIfAbsent
, which is a perfect fit for your use case.
Upvotes: 2