I wrote a collector that would take a stream of Map.Entry<Integer, MyObject>
which I want to make into Map<Integer, Set<MyObject>>
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
class MyObject {
}
class Main {
public static void main(String[] args) {
Map<Integer, Set<MyObject>> collected = Stream.of(
Map.entry(123, new MyObject()),
Map.entry(123, new MyObject()),
Map.entry(234, new MyObject()),
Map.entry(345, new MyObject())
)
.collect(
ConcurrentHashMap<Integer, Set<MyObject>>::new,
(map, entry) -> {
map.computeIfAbsent(
entry.getKey(),
k -> new HashSet<MyObject>()
)
.add(entry.getValue());
},
(map1, map2) -> {
map2.forEach((map2Key, map2Object) -> {
final Set<MyObject> objects =
map1.computeIfAbsent(map2Key, k -> new HashSet<MyObject>());
objects.addAll(map2Object);
});
}
);
System.out.println(collected);
}
}
I was wondering if there's a built in way of doing this, as it seems to be a common thing to do.
One way I can refactor this is to move the collector out to a different class like so:
class EntryToMapCollector<K, V> implements Collector<Map.Entry<K, V>, Map<K, Collection<V>>, Map<K, Collection<V>>> {
@Override
public Supplier<Map<K, Collection<V>>> supplier() {
return ConcurrentHashMap<K, Collection<V>>::new;
}
@Override
public BiConsumer<Map<K, Collection<V>>, Map.Entry<K, V>> accumulator() {
return (map, entry) -> {
map.computeIfAbsent(entry.getKey(), k -> new HashSet<V>())
.add(entry.getValue());
};
}
@Override
public BinaryOperator<Map<K, Collection<V>>> combiner() {
return (map1, map2) -> {
final Map<K, Collection<V>> combined = new ConcurrentHashMap<>(map1);
map2.forEach((map2Key, map2Observations) -> {
combined.computeIfAbsent(map2Key, k -> new HashSet<V>())
.addAll(map2Observations);
});
return combined;
};
}
@Override
public Function<Map<K, Collection<V>>, Map<K, Collection<V>>> finisher() {
return m -> m;
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.CONCURRENT, Characteristics.UNORDERED);
}
}
and replace the call with .collect(new EntryToMapCollector<Integer, MyObject>());