通过项目的属性索引Collection的最干净方法,该项目本身就是一个集合
我有一个List
,想要一个guava Multimap
,我们用它们的Collection getTags()
函数的每个标签对Foo
分组。
我正在使用java 8,所以lambdas和方法引用很好/鼓励。
例如,如果我有:
foo1, tags=a,b,c foo2, tags=c,d foo3, tags=a,c,e
我会得到一个Multimap
:
a -> foo1, foo3 b -> foo1 c -> foo1, foo2, foo3 d -> foo2 e -> foo3
您可以使用自定义收集器:
Multimap map = list.stream().collect( ImmutableMultimap::builder, (builder, value) -> value.getTags().forEach(tag -> builder.put(tag, value)), (builder1, builder2) -> builder1.putAll(builder2.build()) ).build();
这不会引起额外的副作用(参见此处 ),并发且更具惯用性。
您还可以将这些特殊的lambda提取到一个成熟的收集器中,如下所示:
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(); } }
然后集合看起来像这样:
Multimap map = list.stream().collect(toMultimapByKey(Foo::getTags));
如果顺序对您不重要,您还可以从characteristics()
方法返回EnumSet.of(Characteristics.UNORDERED)
。 这可以使内部收集机械更有效地发挥作用,特别是在并行减少的情况下。
ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); list.forEach(foo -> foo.getTags().forEach(tag -> builder.put(tag, foo)); return builder.build();