2014-04-11 12 views
11

ist Ich habe ein List<Foo> und will eine Guave Multimap<String, Foo>, wo wir die Foo ‚s von jedem Tag ihrer Collection<String> getTags() Funktion gruppiert haben.sauberste Art und Weise eine Sammlung durch eine Eigenschaft des Elements zu indizieren, dass ich eine Sammlung

Ich benutze Java 8, also lambdas und Methodenreferenzen sind gut/ermutigt.

Zum Beispiel, wenn ich:

foo1, tags=a,b,c 
foo2, tags=c,d 
foo3, tags=a,c,e 

Ich würde eine bekommen Multimap<String, Foo> mit:

Multimap<String, Foo> map = list.stream().collect(
    ImmutableMultimap::builder, 
    (builder, value) -> value.getTags().forEach(tag -> builder.put(tag, value)), 
    (builder1, builder2) -> builder1.putAll(builder2.build()) 
).build(); 

Dieses:

a -> foo1, foo3 
b -> foo1 
c -> foo1, foo2, foo3 
d -> foo2 
e -> foo3 
+2

Seit Guava 21 gibt es eine integrierte [ImmutableListMultimap.toImmutableListMultimap] (https://google.github.io/guava/releases/21.0/api/docs/com/google/common/collect/ImmutableListMultimap.html# toImmutableListMultimap-java.util.function.Function-java.util.function.Function-) collector. –

Antwort

12

Sie benutzerdefinierte Collector für diesen Einsatz verursacht nicht Die zusätzlichen Nebenwirkungen (siehe here) sind gleichzeitig und idiomatisch.

Sie auch diese Ad-hoc-Lambda-Ausdrücke in einen vollwertigen Sammler, so etwas wie dieses extrahieren:

Multimap<String, Foo> map = list.stream().collect(toMultimapByKey(Foo::getTags)); 

Sie auch zurückkehren können:

public static <T, K> Collector<T, ?, Multimap<K, T>> toMultimapByKey(Function<? super T, ? extends Iterable<? extends K>> keysMapper) { 
    return new MultimapCollector<>(keysMapper); 
} 

private static class MultimapCollector<T, K> implements Collector<T, ImmutableMultimap.Builder<K, T>, Multimap<K, T>> { 
    private final Function<? super T, ? extends Iterable<? extends K>> keysMapper; 

    private MultimapCollector(Function<? super T, ? extends Iterable<? extends K>> keysMapper) { 
     this.keysMapper = keysMapper; 
    } 

    @Override 
    public Supplier<ImmutableMultimap.Builder<K, T>> supplier() { 
     return ImmutableMultimap::builder; 
    } 

    @Override 
    public BiConsumer<ImmutableMultimap.Builder<K, T>, T> accumulator() { 
     return (builder, value) -> keysMapper.apply(value).forEach(k -> builder.put(k, value)); 
    } 

    @Override 
    public BinaryOperator<ImmutableMultimap.Builder<K, T>> combiner() { 
     return (b1, b2) -> b1.putAll(b2.build()); 
    } 

    @Override 
    public Function<ImmutableMultimap.Builder<K, T>, Multimap<K, T>> finisher() { 
     return ImmutableMultimap.Builder<K, T>::build; 
    } 

    @Override 
    public Set<Characteristics> characteristics() { 
     return Collections.emptySet(); 
    } 
} 

Dann wird die Sammlung würde wie folgt aussehen EnumSet.of(Characteristics.UNORDERED) von characteristics() Methode, wenn die Reihenfolge für Sie nicht wichtig ist. Dies kann dazu führen, dass interne Sammlungsmaschinen effizienter arbeiten, insbesondere im Fall einer parallelen Reduktion.

7
ImmutableMultimap.Builder<String, Foo> builder = ImmutableMultimap.builder(); 
list.forEach(foo -> foo.getTags().forEach(tag -> builder.put(tag, foo)); 
return builder.build(); 
+0

Nett, und kann in einem netten statischen Helfer eingewickelt werden. Neugierig, wenn es etwas kürzeres gibt. –

+0

Nicht in dieser Version von Guava. –

Verwandte Themen