ventajas objetos mutables instancias inmutables inmutable inmutabilidad ejemplos clases java guava java-stream

instancias - objetos mutables e inmutables java



¿Java 8 coleccionista para colecciones inmutables de guayaba? (5)

Realmente me gustan las secuencias de Java 8 y las colecciones inmutables de Guava, pero no puedo entender cómo usar los dos juntos.

Por ejemplo, ¿cómo implemento un Java 8 Collector que recopila resultados de flujo en un ImmutableMultimap ?

Puntos de bonificación: me gustaría poder proporcionar asignadores de clave / valor, similar a cómo funciona Collectors.toMap() .


Aquí hay una versión que soportará varias implementaciones de ImmutableMultimap . Tenga en cuenta que el primer método es privado ya que requiere una conversión no segura.

@SuppressWarnings("unchecked") private static <T, K, V, M extends ImmutableMultimap<K, V>> Collector<T, ?, M> toImmutableMultimap( Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends V> valueFunction, Supplier<? extends ImmutableMultimap.Builder<K, V>> builderSupplier) { return Collector.of( builderSupplier, (builder, element) -> builder.put(keyFunction.apply(element), valueFunction.apply(element)), (left, right) -> { left.putAll(right.build()); return left; }, builder -> (M)builder.build()); } public static <T, K, V> Collector<T, ?, ImmutableMultimap<K, V>> toImmutableMultimap( Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends V> valueFunction) { return toImmutableMultimap(keyFunction, valueFunction, ImmutableMultimap::builder); } public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap( Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends V> valueFunction) { return toImmutableMultimap(keyFunction, valueFunction, ImmutableListMultimap::builder); } public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap( Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends V> valueFunction) { return toImmutableMultimap(keyFunction, valueFunction, ImmutableSetMultimap::builder); }


Aunque no responde específicamente a la pregunta, cabe mencionar que este patrón simple puede ayudar a todos a crear colecciones inmutables de Guava a partir de flujos sin tener la necesidad de Collector (ya que sus implementaciones son bastante difíciles de hacer).

Stream<T> stream = ... ImmutableXxx<T> collection = ImmutableXxx.copyOf(stream.iterator());


Desde la versión 21, puedes

.collect(ImmutableSet.toImmutableSet()) .collect(Maps.toImmutableEnumMap()) .collect(Sets.toImmutableEnumSet()) .collect(Tables.toTable()) .collect(ImmutableList.toImmutableList()) .collect(Multimaps.toMultimap(...))


Para su ejemplo, puede usar el recopilador toImmutableMap () de Guava. P.ej

import static com.google.common.collect.ImmutableMap.toImmutableMap; ImmutableMap<String, ZipCode> zipCodeForName = people.stream() .collect( toImmutableMap(Person::getName, p -> p.getAddress().getZipCode())); ImmutableMap<String, Person> personForName = people.stream() .collect( toImmutableMap(Person::getName, p -> p));

La respuesta principal tiene el resto de las API de recopiladores inmutables de guava.


Actualización : encontré una implementación que parece cubrir todas las colecciones de guayas en https://github.com/yanaga/guava-stream e intenté mejorarla en mi propia biblioteca en https://bitbucket.org/cowwoc/guava-jdk8/

Dejo la respuesta anterior a continuación, por razones históricas.

Santo # @! (Lo tengo!

Esta implementación funciona para cualquier Multimap (mutable o inmutable), mientras que la solución de shmosel se enfoca en implementaciones inmutables. Dicho esto, este último podría ser más eficiente para el caso inmutable (no uso un constructor).

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); } }

salidas principales ():

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

Recursos

  • Arjit proporcionó un excelente recurso que demuestra cómo implementar los recopiladores para otras colecciones de guayabas: blog.comsysto.com/2014/11/12/…