从 Java8流创建 GuavaMultimap 的最干净的方法

我有一个 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>,所以这并没有为我节省任何东西,我回到使用 for 循环来创建我的 Multimap。我是不是错过了什么“实用”的好方法?

66227 次浏览

你可以直接使用番石榴 Multimaps工厂:

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

或者使用 Collector<T, A, R>接口包装对 Multimaps.index的调用(如下所示,在未经优化的初始实现中)。

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

Guava 21.0引入了几种返回 Collector实例的方法,这些方法将 Stream转换为 Multimap,并根据对其元素应用函数的结果进行分组。这些方法是:

ImmutableListMultimap<String, Foo> foosById = foos.stream().collect(
ImmutableListMultimap.toImmutableListMultimap(
Foo::getId, Function.identity()));
ImmutableSetMultimap<String, Foo> foosById = foos.stream().collect(
ImmutableSetMultimap.toImmutableSetMultimap(
Foo::getId, Function.identity()));
HashMultimap<String, Foo> foosById = foos.stream().collect(
Multimaps.toMultimap(
Foo::getId, Function.identity(), HashMultimap::create)
);