1. ホーム
  2. java

Java 8 ストリームから Guava Multimap を作成する最もクリーンな方法

2023-12-10 22:45:01

質問

私は List<Foo> があり Multimap<String, Foo> をグループ化したところで Foo をグループ化しています。 getId() の機能によって決定されます。

私はJava8を使用していますが、そのできることはほとんど素晴らしいです。

List<Foo> foos = ...
Map<String, List<Foo>> foosById = foos.stream().collect(groupingBy(Foo::getId));

しかし、私のコードの中には MultiMap<String, Foo> を必要とするコードが大量にあるので、これでは何の節約にもなりません。 Multimap . 私が見逃している素敵なquot;functional"方法はありますか?

どのように解決するのですか?

単にGuavaを使えばいいのです。 Multimaps ファクトリを使用します。

ImmutableMultimap<String, Foo> foosById = Multimaps.index(foos, Foo::getId);

への呼び出しをラップするか Multimaps.index Collector<T, A, R> インタフェースがあります (最適化されていない素朴な実装を以下に示します)。

Multimap<String, Foo> collect = foos.stream()
        .collect(MultimapCollector.toMultimap(Foo::getId));

で、その Collector :

public class MultimapCollector<T, K, V> implements Collector<T, Multimap<K, V>, Multimap<K, V>> {

    private final Function<T, K> keyGetter;
    private final Function<T, V> valueGetter;

    public MultimapCollector(Function<T, K> keyGetter, Function<T, V> valueGetter) {
        this.keyGetter = keyGetter;
        this.valueGetter = valueGetter;
    }

    public static <T, K, V> MultimapCollector<T, K, V> toMultimap(Function<T, K> keyGetter, Function<T, V> valueGetter) {
        return new MultimapCollector<>(keyGetter, valueGetter);
    }

    public static <T, K, V> MultimapCollector<T, K, T> toMultimap(Function<T, K> keyGetter) {
        return new MultimapCollector<>(keyGetter, v -> v);
    }

    @Override
    public Supplier<Multimap<K, V>> supplier() {
        return ArrayListMultimap::create;
    }

    @Override
    public BiConsumer<Multimap<K, V>, T> accumulator() {
        return (map, element) -> map.put(keyGetter.apply(element), valueGetter.apply(element));
    }

    @Override
    public BinaryOperator<Multimap<K, V>> combiner() {
        return (map1, map2) -> {
            map1.putAll(map2);
            return map1;
        };
    }

    @Override
    public Function<Multimap<K, V>, Multimap<K, V>> finisher() {
        return map -> map;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return ImmutableSet.of(Characteristics.IDENTITY_FINISH);
    }
}