Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 collector for Guava immutable collections?

Tags:

I really like Java 8 streams and Guava's immutable collections, but I can't figure out how to use the two together.

For example, how do I implement a Java 8 Collector that gathers stream results into an ImmutableMultimap?

Bonus points: I'd like to be able to provide key/value mappers, similar to how Collectors.toMap() works.

like image 859
Gili Avatar asked Jun 04 '15 03:06

Gili


People also ask

How do you make a collection immutable in Java 8?

In Java 8 and earlier versions, we can use collection class utility methods like unmodifiableXXX to create immutable collection objects. If we need to create an immutable list then use the Collections. unmodifiableList() method.

Is toList immutable?

toList() returns an immutable list. So, trying to add a new element to the list will simply lead to UnsupportedOperationException.

How do you get immutable object of a collection?

So, basically, in order to get an immutable collection out of a mutable one, you have to copy its elements to the new collection, and disallow all operations.

How do you make a list object immutable using streams API?

List<Person> immutableList = Collections. unmodifiableList(new ArrayList<>(persons)); This creates an immutable list since a conversion constructor is being used. No add/remove/set ops can be performed on immutableList nor any change in the original list persons would reflect in immutableList .


2 Answers

Since version 21, you can

.collect(ImmutableSet.toImmutableSet()) .collect(ImmutableMap.toImmutableMap()) .collect(Maps.toImmutableEnumMap()) .collect(Sets.toImmutableEnumSet()) .collect(Tables.toTable()) .collect(ImmutableList.toImmutableList()) .collect(Multimaps.toMultimap(...)) 
like image 100
numéro6 Avatar answered Oct 01 '22 23:10

numéro6


Update: I found an implementation that seems to cover all Guava collections at https://github.com/yanaga/guava-stream and attempted to improve upon it in my own library at https://bitbucket.org/cowwoc/guava-jdk8/

I'm leaving the previous answer below, for historical reasons.


Holy #@!( I got it!

This implementation works for any Multimap (mutable or immutable) whereas shmosel's solution focuses on immutable implementations. That said, the latter might be more efficient for the immutable case (I don't use a builder).

import com.google.common.collect.Multimap; import java.util.EnumSet; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Collector.Characteristics; import org.bitbucket.cowwoc.preconditions.Preconditions;  /**  * A Stream collector that returns a Multimap.  * <p>  * @author Gili Tzabari  * @param <T> the type of the input elements  * @param <K> the type of keys stored in the map  * @param <V> the type of values stored in the map  * @param <R> the output type of the collector  */ public final class MultimapCollector<T, K, V, R extends Multimap<K, V>>     implements Collector<T, Multimap<K, V>, R> {     private final Supplier<Multimap<K, V>> mapSupplier;     private final Function<? super T, ? extends K> keyMapper;     private final Function<? super T, ? extends V> valueMapper;     private final Function<Multimap<K, V>, R> resultMapper;      /**      * Creates a new MultimapCollector.      * <p>      * @param mapSupplier  a function which returns a new, empty {@code Multimap} into which intermediate results will be      *                     inserted      * @param keyMapper    a function that transforms the map keys      * @param valueMapper  a function that transforms the map values      * @param resultMapper a function that transforms the intermediate {@code Multimap} into the final result      * @throws NullPointerException if any of the arguments are null      */     public MultimapCollector(Supplier<Multimap<K, V>> mapSupplier,         Function<? super T, ? extends K> keyMapper,         Function<? super T, ? extends V> valueMapper,         Function<Multimap<K, V>, R> resultMapper)     {         Preconditions.requireThat(mapSupplier, "mapSupplier").isNotNull();         Preconditions.requireThat(keyMapper, "keyMapper").isNotNull();         Preconditions.requireThat(valueMapper, "valueMapper").isNotNull();         Preconditions.requireThat(resultMapper, "resultMapper").isNotNull();          this.mapSupplier = mapSupplier;         this.keyMapper = keyMapper;         this.valueMapper = valueMapper;         this.resultMapper = resultMapper;     }      @Override     public Supplier<Multimap<K, V>> supplier()     {         return mapSupplier;     }      @Override     public BiConsumer<Multimap<K, V>, T> accumulator()     {         return (map, entry) ->         {             K key = keyMapper.apply(entry);             if (key == null)                 throw new IllegalArgumentException("keyMapper(" + entry + ") returned null");             V value = valueMapper.apply(entry);             if (value == null)                 throw new IllegalArgumentException("keyMapper(" + entry + ") returned null");             map.put(key, value);         };     }      @Override     public BinaryOperator<Multimap<K, V>> combiner()     {         return (left, right) ->         {             left.putAll(right);             return left;         };     }      @Override     public Function<Multimap<K, V>, R> finisher()     {         return resultMapper;     }      @Override     public Set<Characteristics> characteristics()     {         return EnumSet.noneOf(Characteristics.class);     } } 

[...]

import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector;  /**  * Stream collectors for Guava collections.  * <p>  * @author Gili Tzabari  */ public final class GuavaCollectors {     /**      * Returns a {@code Collector} that accumulates elements into a {@code Multimap}.      * <p>      * @param <T>          the type of the input elements      * @param <K>          the type of the map keys      * @param <V>          the type of the map values      * @param <R>          the output type of the collector      * @param mapSupplier  a function which returns a new, empty {@code Multimap} into which intermediate results will be      *                     inserted      * @param keyMapper    a function that transforms the map keys      * @param valueMapper  a function that transforms the map values      * @param resultMapper a function that transforms the intermediate {@code Multimap} into the final result      * @return a {@code Collector} which collects elements into a {@code Multimap} whose keys and values are the result of      *         applying mapping functions to the input elements      */     public static <T, K, V, R extends Multimap<K, V>> Collector<T, ?, R> toMultimap(         Supplier<Multimap<K, V>> mapSupplier,         Function<? super T, ? extends K> keyMapper,         Function<? super T, ? extends V> valueMapper,         Function<Multimap<K, V>, R> resultMapper)     {         return new MultimapCollector<>(mapSupplier, keyMapper, valueMapper, resultMapper);     }      public static void main(String[] args)     {         Multimap<Integer, Double> input = HashMultimap.create();         input.put(10, 20.0);         input.put(10, 25.0);         input.put(50, 60.0);         System.out.println("input: " + input);         ImmutableMultimap<Integer, Double> output = input.entries().stream().collect(             GuavaCollectors.toMultimap(HashMultimap::create,                 entry -> entry.getKey() + 1, entry -> entry.getValue() - 1,                 ImmutableMultimap::copyOf));         System.out.println("output: " + output);     } } 

main() outputs:

input: {10=[20.0, 25.0], 50=[60.0]} output: {51=[59.0], 11=[24.0, 19.0]} 

Resources

  • Arjit provided an excellent resource demonstrating how to implement Collectors for other Guava collections: http://blog.comsysto.com/2014/11/12/java-8-collectors-for-guava-collections/
like image 36
Gili Avatar answered Oct 01 '22 22:10

Gili