I have a List<Foo>
and want a Multimap<String, Foo>
where we've grouped the Foo
's by their getId()
function.
I am using Java 8 and its almost awesome in that you can do:
List<Foo> foos = ... Map<String, List<Foo>> foosById = foos.stream().collect(groupingBy(Foo::getId));
However, I have a good amount of code that wants a MultiMap<String, Foo>
so this doesnt save me anything and I'm back to using a for-loop to create my Multimap
. Is there a nice "functional" way that I am missing?
Following is a simple custom implementation of the Multimap class in Java using a Map and a Collection . * Add the specified value with the specified key in this multimap. * Returns the Collection of values to which the specified key is mapped, * or null if this multimap contains no mapping for the key.
Returns a synchronized (thread-safe) SortedSetMultimap backed by the specified multimap.
A Multimap is a new collection type that is found in Google's Guava library for Java. A Multimap can store more than one value against a key. Both the keys and the values are stored in a collection, and considered to be alternates for Map<K, List<V>> or Map<K, Set<V>> (standard JDK Collections Framework).
The ArrayListMultimap is a variation of a Map in which multiple values or objects are associated with a single key but it allows duplicate key/value pairs in the Map.
You can just use the Guava Multimaps
factory:
ImmutableMultimap<String, Foo> foosById = Multimaps.index(foos, Foo::getId);
or wrap a call to Multimaps.index
with a Collector<T, A, R>
interface (shown below, in an unoptimized naive implementation).
Multimap<String, Foo> collect = foos.stream() .collect(MultimapCollector.toMultimap(Foo::getId));
and the Collector
:
public class MultimapCollector<T, K, V> implements Collector<T, Multimap<K, V>, Multimap<K, V>> { private final Function<T, K> keyGetter; private final Function<T, V> valueGetter; public MultimapCollector(Function<T, K> keyGetter, Function<T, V> valueGetter) { this.keyGetter = keyGetter; this.valueGetter = valueGetter; } public static <T, K, V> MultimapCollector<T, K, V> toMultimap(Function<T, K> keyGetter, Function<T, V> valueGetter) { return new MultimapCollector<>(keyGetter, valueGetter); } public static <T, K, V> MultimapCollector<T, K, T> toMultimap(Function<T, K> keyGetter) { return new MultimapCollector<>(keyGetter, v -> v); } @Override public Supplier<Multimap<K, V>> supplier() { return ArrayListMultimap::create; } @Override public BiConsumer<Multimap<K, V>, T> accumulator() { return (map, element) -> map.put(keyGetter.apply(element), valueGetter.apply(element)); } @Override public BinaryOperator<Multimap<K, V>> combiner() { return (map1, map2) -> { map1.putAll(map2); return map1; }; } @Override public Function<Multimap<K, V>, Multimap<K, V>> finisher() { return map -> map; } @Override public Set<Characteristics> characteristics() { return ImmutableSet.of(Characteristics.IDENTITY_FINISH); } }
Guava 21.0 introduced several methods that return Collector
instances which will convert a Stream
into a Multimap
grouped by the result of applying a function to its elements. These methods are:
ImmutableListMultimap.toImmutableListMultimap
— creates an ImmutableListMultimap
ImmutableSetMultimap.toImmutableSetMultimap
— creates an ImmutableSetMultimap
Multimaps.toMultimap
— Creates a Multimap
using the given Supplier
ImmutableListMultimap<String, Foo> foosById = foos.stream().collect( ImmutableListMultimap.toImmutableListMultimap( Foo::getId, Function.identity()));
ImmutableSetMultimap<String, Foo> foosById = foos.stream().collect( ImmutableSetMultimap.toImmutableSetMultimap( Foo::getId, Function.identity()));
HashMultimap<String, Foo> foosById = foos.stream().collect( Multimaps.toMultimap( Foo::getId, Function.identity(), HashMultimap::create) );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With