I have a List and want a Guava MultimapFoos by each tag of their Collection
You can use custom collector for this:
Multimap map = list.stream().collect(
ImmutableMultimap::builder,
(builder, value) -> value.getTags().forEach(tag -> builder.put(tag, value)),
(builder1, builder2) -> builder1.putAll(builder2.build())
).build();
This does not cause extra side effects (see here on this), is concurrent and more idiomatic.
You can also extract these ad-hoc lambdas into a full-fledged collector, something like this:
public static Collector> toMultimapByKey(Function super T, ? extends Iterable extends K>> keysMapper) {
return new MultimapCollector<>(keysMapper);
}
private static class MultimapCollector implements Collector, Multimap> {
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> supplier() {
return ImmutableMultimap::builder;
}
@Override
public BiConsumer, T> accumulator() {
return (builder, value) -> keysMapper.apply(value).forEach(k -> builder.put(k, value));
}
@Override
public BinaryOperator> combiner() {
return (b1, b2) -> b1.putAll(b2.build());
}
@Override
public Function, Multimap> finisher() {
return ImmutableMultimap.Builder::build;
}
@Override
public Set characteristics() {
return Collections.emptySet();
}
}
Then the collection would look like this:
Multimap map = list.stream().collect(toMultimapByKey(Foo::getTags));
You can also return EnumSet.of(Characteristics.UNORDERED) from characteristics() method if the order is not important for you. This can make internal collection machinery act more efficiently, especially in case of parallel reduction.