简体   繁体   English

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

[英]Java 8 collector for Guava immutable collections?

I really like Java 8 streams and Guava's immutable collections, but I can't figure out how to use the two together.我真的很喜欢 Java 8 流和 Guava 的不可变集合,但我不知道如何将两者一起使用。

For example, how do I implement a Java 8 Collector that gathers stream results into an ImmutableMultimap ?例如,如何实现将流结果收集到ImmutableMultimap的 Java 8 收集器

Bonus points: I'd like to be able to provide key/value mappers, similar to how Collectors.toMap() works.加分项:我希望能够提供键/值映射器,类似于Collectors.toMap() 的工作方式。

Since version 21, you can从版本 21 开始,您可以

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

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/更新:我在https://github.com/yanaga/guava-stream 上找到了一个似乎涵盖所有番石榴集合的实现,并试图在我自己的库中对其进行改进,网址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.此实现适用于任何 Multimap(可变或不可变),而shmosel 的解决方案侧重于不可变实现。 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: main() 输出:

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

Resources资源

Here's a version that will support several ImmutableMultimap implementations.这是一个支持多个ImmutableMultimap实现的版本。 Note that the first method is private since it requires an unsafe cast.请注意,第一个方法是私有的,因为它需要一个不安全的转换。

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

For your example, you can use Guava's toImmutableMap() collector.对于您的示例,您可以使用 Guava 的 toImmutableMap() 收集器。 Eg例如

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

The top answer has the rest of the Guava immutable collectors APIs最佳答案是其余的 Guava 不可变收集器 API

Although it doesn't answer specifically the question, it's noteworthy to mention that this simple pattern can help everyone building Guava's immutable collections from streams without having the need for Collector s (as their implementations are rather hard to make).虽然它没有具体回答这个问题,但值得注意的是,这个简单的模式可以帮助每个人从流中构建 Guava 的不可变集合,而无需Collector s(因为它们的实现很难实现)。

Stream<T> stream = ...

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM