【问题标题】:Java 8 collector for Guava immutable collections?用于 Guava 不可变集合的 Java 8 收集器?
【发布时间】:2015-06-04 03:46:39
【问题描述】:

我真的很喜欢 Java 8 流和 Guava 的不可变集合,但我不知道如何将两者结合使用。

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

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

【问题讨论】:

标签: java guava java-stream


【解决方案1】:

从版本 21 开始,您可以

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

【讨论】:

    【解决方案2】:

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

    出于历史原因,我将在下面留下之前的答案。


    神圣的#@!(我明白了!

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

    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() 输出:

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

    资源

    【讨论】:

    • +1 将您的解决方案发布为可重用库,我们可以将其添加为简单的 Maven 依赖项! :-) 这看起来只是我一直在寻找的权宜之计解决方案,直到 Guava 21 内置(2016 年中)推出为止
    【解决方案3】:

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

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

    【讨论】:

    • 很好的解决方案,但由于使用了ImmutableMultimap.Builder,它仅限于不可变的实现。似乎不可能提供一个单独的 Collector 来为可变和不可变类型做正确的事情。我的答案支持这两种类型,但它不使用Builder,因此它对于不可变集合的效率可能较低。
    • @Gili,这个问题清楚地表明您正在寻找不可变的收藏家。您的答案本身不支持不可变类型;它依赖于这样一个假设:一个可变的结果可以随后被复制到一个不可变的集合中。就性能而言,我的方法对于顺序流应该更有效,而对于并行流,使用可变累加器可能更可取。
    【解决方案4】:

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

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

    top answer 拥有其余的 Guava 不可变收集器 API

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

      • 这将不支持其他收集器中标准的任何自定义映射或分组功能。
      猜你喜欢
      • 1970-01-01
      • 2017-07-03
      • 2016-12-30
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 2014-06-11
      相关资源
      最近更新 更多