通过项目的属性索引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> keysMapper) { return new MultimapCollector<>(keysMapper); } private static class MultimapCollector implements Collector, Multimap> { private final Function> keysMapper; private MultimapCollector(Function> 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();