Java 8 collector pour des collections immuables de goyaves?
J'aime vraiment Java 8 streams et les collections immuables de Guava, mais je ne peux pas comprendre comment utiliser les deux ensemble.
par exemple, comment implémenter un Java 8 Collecteur qui rassemble les résultats de flux dans un ImmutableMultimap<!--4?
points Bonus: j'aimerais être en mesure de fournir des mappeurs de clés/valeurs, similaire à comment les Collectionneurs.toMap () fonctionne.
5 réponses
depuis la version 21, vous pouvez
.collect(ImmutableSet.toImmutableSet())
.collect(Maps.toImmutableEnumMap())
.collect(Sets.toImmutableEnumSet())
.collect(Tables.toTable())
.collect(ImmutableList.toImmutableList())
.collect(Multimaps.toMultimap(...))
mise à Jour: j'ai trouvé une implémentation qui semble couvrir toutes les collections de goyaves à https://github.com/yanaga/guava-stream et tenté de l'améliorer dans ma propre bibliothèque à https://bitbucket.org/cowwoc/guava-jdk8/
je laisse la réponse précédente ci-dessous, pour des raisons historiques.
Saint -#@!( Je l'ai eu!
cette implémentation fonctionne pour tout Multimap (mutable ou immuable) alors que solution de shmosel se concentre sur les implémentations immuables. Cela dit, ce dernier pourrait être plus efficace pour l'immuable cas (je n'utilise pas un constructeur).
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() sorties:
input: {10=[20.0, 25.0], 50=[60.0]}
output: {51=[59.0], 11=[24.0, 19.0]}
Ressources
- Arjit a fourni une excellente ressource démontrant comment mettre en œuvre des collecteurs pour d'autres collections de goyaves: http://blog.comsysto.com/2014/11/12/java-8-collectors-for-guava-collections/
Voici une version qui supporte plusieurs ImmutableMultimap
implémentations. Notez que la première méthode est privée puisqu'elle nécessite un moulage dangereux.
@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);
}
bien qu'il ne réponde pas spécifiquement à la question, il est intéressant de mentionner que ce modèle simple peut aider tout le monde de construire des collections immuables de goyaves à partir de ruisseaux sans avoir besoin de Collector
s (car leurs implémentations sont assez difficiles à faire).
Stream<T> stream = ...
ImmutableXxx<T> collection = ImmutableXxx.copyOf(stream.iterator());
pour votre exemple, vous pouvez utiliser le collector toImmutableMap() de Guava. E. g.
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));
réponse a le reste des collecteurs immuables De Goyave APIs