I have a List<Foo>
and want a guava Multimap<String, Foo>
where we've grouped the Foo
's by each tag of their Collection<String> getTags()
function.
我有一个列表
I am using java 8, so lambdas and method references are fine/encouraged.
我正在使用java 8,所以lambdas和方法引用是很好的。
For example if I have:
例如,如果我有:
foo1, tags=a,b,c
foo2, tags=c,d
foo3, tags=a,c,e
I would get a Multimap<String, Foo>
with:
我会得到一个多映射
a -> foo1, foo3
b -> foo1
c -> foo1, foo2, foo3
d -> foo2
e -> foo3
2 个解决方案
#1
12
You can use custom collector for this:
您可以使用自定义收集器:
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();
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:
您还可以将这些特殊的lambdas提取到一个成熟的收集器中,如下所示:
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();
}
}
Then the collection would look like this:
那么,这些藏品会是这样的:
Multimap<String, Foo> 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.
如果顺序对您不重要,您还可以从特性()方法返回枚举。这可以使内部收集机器更有效地工作,特别是在并行还原的情况下。
#2
7
ImmutableMultimap.Builder<String, Foo> builder = ImmutableMultimap.builder();
list.forEach(foo -> foo.getTags().forEach(tag -> builder.put(tag, foo));
return builder.build();
#1
12
You can use custom collector for this:
您可以使用自定义收集器:
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();
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:
您还可以将这些特殊的lambdas提取到一个成熟的收集器中,如下所示:
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();
}
}
Then the collection would look like this:
那么,这些藏品会是这样的:
Multimap<String, Foo> 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.
如果顺序对您不重要,您还可以从特性()方法返回枚举。这可以使内部收集机器更有效地工作,特别是在并行还原的情况下。
#2
7
ImmutableMultimap.Builder<String, Foo> builder = ImmutableMultimap.builder();
list.forEach(foo -> foo.getTags().forEach(tag -> builder.put(tag, foo));
return builder.build();