从Java8流创建番石榴多重映射的最干净方法

从Java8流创建番石榴多重映射的最干净方法,java,java-stream,guava,Java,Java Stream,Guava,我有一个列表,需要一个多映射,在这里我们按照getId()函数对Foo进行分组 我正在使用Java 8,它几乎令人敬畏,因为您可以: List<Foo> foos = ... Map<String, List<Foo>> foosById = foos.stream().collect(groupingBy(Foo::getId)); listfoos=。。。 Map foosById=foos.stream().collect(groupingBy(Foo

我有一个
列表
,需要一个
多映射
,在这里我们按照
getId()
函数对
Foo
进行分组

我正在使用Java 8,它几乎令人敬畏,因为您可以:

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

但是,我有大量的代码需要一个
MultiMap
,因此这不会为我节省任何东西,我又回到使用for循环来创建我的
MultiMap
。有没有我缺少的“功能性”方法?

您可以使用Guava Multimaps工厂:

Multimaps.index(foos, Foo::getId);
或者使用收集器接口包装对Multimaps.index的调用(如下所示,在未优化的朴素实现中)

Multimap collect=foos.stream().collect(MultimapCollector.toMultimap(Foo::getId));
而收藏家:

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);
    }
}
公共类MultimapCollector实现收集器{
私有最终函数keyGetter;
私人最终职能估价师;
公共MultimapCollector(函数keyGetter、函数valueGetter){
this.keyGetter=keyGetter;
this.valueGetter=valueGetter;
}
公共静态MultimapCollector到MultiMap(函数keyGetter、函数valueGetter){
返回新的MultimapCollector(keyGetter、valueGetter);
}
公共静态MultimapCollector到MultiMap(函数keyGetter){
返回新的MultimapCollector(keyGetter,v->v);
}
@凌驾
公共供应商(){
返回ArrayListMultimap::create;
}
@凌驾
公共双消费者累加器(){
return(map,element)->map.put(keyGetter.apply(element),valueGetter.apply(element));
}
@凌驾
公共二进制运算符组合器(){
返回(map1、map2)->{
map1.putAll(map2);
返回map1;
};
}
@凌驾
公共函数完成器(){
返回地图->地图;
}
@凌驾
公共集特征(){
返回ImmutableSet.of(Characteristics.IDENTITY\u FINISH);
}
}

这不是一个重复的问题,因为另一个问题是通过他们的属性(即数组(标记))来索引Foo,而不是从Guava 21.0开始的具有单个值(id)的属性。有一个内置的和返回收集器的方法。投票重新打开,因为这不是链接问题的重复。这一个是API的直接使用(如已接受的答案中所述),而另一个更多的是转换和平面映射值,而不是实际的集合本身。
Multimaps.index
返回一个不可变的
Multimap
,这可能不是您想要的。同意,Multimap本身是不可变的,但这并不意味着它们中包含的对象会自动被删除。如果我们可以获得对不可变集合中对象的引用,那么没有什么可以阻止我们更改该对象上的任何可变状态。因此,对于某些用例,Multimaps.index仍然很好。我的2美分。在大多数情况下,不可变的多重映射正是您所需要的。我编写了一个不可变的多重映射收集器:
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);
    }
}