2014-04-11 7 views
11

Mam List<Foo> i chcesz guawa Multimap<String, Foo> gdzie mamy podzieliła Foo „s przez każdego znacznika ich Collection<String> getTags() funkcji.najczystszym sposobem indeksowania Collection przez właściwość elementu, który sam w sobie jest zbiorem

Używam java 8, więc lambdy i odniesienia do metod są dobre/zalecane.

Na przykład jeśli mam:

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

chciałbym uzyskać Multimap<String, Foo> z:

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

Od Guava 21 istnieje wbudowana [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. –

Odpowiedz

12

Można użyć niestandardowego kolektor dla tego:

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

ten nie ma związku przyczynowego Dodatkowe efekty uboczne (patrz: here) są równoczesne i bardziej idiomatyczne.

Można także wyodrębnić te ad-hoc lambdas się pełnoprawnym kolektora, coś takiego:

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

Następnie kolekcja będzie wyglądać następująco:

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

Można również zwrócić EnumSet.of(Characteristics.UNORDERED) z metody characteristics(), jeśli zamówienie nie jest dla Ciebie ważne. Może to sprawić, że wewnętrzne urządzenia zbierające działają wydajniej, szczególnie w przypadku równoległej redukcji.

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

Ładne i można je owinąć w ładną statyczną pomocnika. Ciekawe, jeśli jest coś krótszego. –

+0

Nie w tej wersji Guava. –

Powiązane problemy