用于Guava不可变集合的Java 8收集器?

我非常喜欢Java 8流和Guava的不可变集合,但我无法弄清楚如何将两者结合使用。

例如,如何实现将流结果收集到ImmutableMultimap中的Java 8 Collector ?

加分点:我希望能够提供键/值映射器,类似于Collectors.toMap()的工作方式。

从21版开始,你就可以了

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

更新 :我发现一个实现似乎涵盖了https://github.com/yanaga/guava-stream上的所有Guava集合,并尝试在我自己的库中对其进行改进, url为https://bitbucket.org/cowwoc/guava- jdk8 /

由于历史原因,我将在下面留下上一个答案。


神圣#@!(我明白了!

此实现适用于任何Multimap(可变或不可变),而shmosel的解决方案专注于不可变实现。 也就是说,后者对于不可变的情况可能更有效(我不使用构建器)。

 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. * 

* @author Gili Tzabari * @param the type of the input elements * @param the type of keys stored in the map * @param the type of values stored in the map * @param the output type of the collector */ public final class MultimapCollector> implements Collector, R> { private final Supplier> mapSupplier; private final Function keyMapper; private final Function valueMapper; private final Function, R> resultMapper; /** * Creates a new MultimapCollector. *

* @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> mapSupplier, Function keyMapper, Function valueMapper, Function, 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> supplier() { return mapSupplier; } @Override public BiConsumer, 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> combiner() { return (left, right) -> { left.putAll(right); return left; }; } @Override public Function, R> finisher() { return resultMapper; } @Override public Set 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. * 

* @author Gili Tzabari */ public final class GuavaCollectors { /** * Returns a {@code Collector} that accumulates elements into a {@code Multimap}. *

* @param the type of the input elements * @param the type of the map keys * @param the type of the map values * @param 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 > Collector toMultimap( Supplier> mapSupplier, Function keyMapper, Function valueMapper, Function, R> resultMapper) { return new MultimapCollector<>(mapSupplier, keyMapper, valueMapper, resultMapper); } public static void main(String[] args) { Multimap input = HashMultimap.create(); input.put(10, 20.0); input.put(10, 25.0); input.put(50, 60.0); System.out.println("input: " + input); ImmutableMultimap 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()输出:

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

资源

  • Arjit提供了一个很好的资源,展示了如何为其他Guava集合实现收集器: http : //blog.comsysto.com/2014/11/12/java-8-collectors-for-guava-collections/

这是一个支持几个ImmutableMultimap实现的版本。 请注意,第一种方法是私有的,因为它需要不安全的强制转换。

 @SuppressWarnings("unchecked") private static > Collector toImmutableMultimap( Function keyFunction, Function valueFunction, Supplier> 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  Collector> toImmutableMultimap( Function keyFunction, Function valueFunction) { return toImmutableMultimap(keyFunction, valueFunction, ImmutableMultimap::builder); } public static  Collector> toImmutableListMultimap( Function keyFunction, Function valueFunction) { return toImmutableMultimap(keyFunction, valueFunction, ImmutableListMultimap::builder); } public static  Collector> toImmutableSetMultimap( Function keyFunction, Function valueFunction) { return toImmutableMultimap(keyFunction, valueFunction, ImmutableSetMultimap::builder); } 

虽然它没有特别回答这个问题,但值得一提的是,这个简单的模式可以帮助每个人从流中构建Guava的不可变集合,而不需要Collector (因为它们的实现很难实现)。

 Stream stream = ... ImmutableXxx collection = ImmutableXxx.copyOf(stream.iterator()); 

对于您的示例,您可以使用Guava的toImmutableMap()收集器。 例如

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

最佳答案是其余的Guava不可变收集器API