使用Guava将一个Iterable<Iterable<T>>扁平化

41
有没有在Guava中提供flatten方法或转换Iterable<Iterable<T>>Iterable<T>的简单方法?
我有一个Multimap<K, V> [sourceMultimap],我想返回所有键与某个断言[keyPredicate]匹配的值。目前我的代码是:
Iterable<Collection<V>> vals = Maps.filterKeys(sourceMultimap.asMap(), keyPredicate).values();

Collection<V> retColl = ...;
for (Collection<V> vs : vals) retColl.addAll(vs);
return retColl;

我已经查看了Guava文档,但没有发现任何突出的内容。我只是想确认我没有漏掉什么东西。否则,我将把我的三行代码提取为一个简短的通用方法并将其保留。

2个回答

74

Iterables.concat方法满足这个要求:

public static <T> Iterable<T> concat(Iterable<? extends Iterable<? extends T>> inputs)

6
我猜这是因为这仅仅是一级拼接,而不是真正的扁平化 :) - Gabriel Ščerbák
此外,如果正在从Guava transform生成集合,则可以使用FluentIterable transformAndConcat:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/FluentIterable.html#transformAndConcat(com.google.common.base.Function)。 - Luís Bianchin

4

从Java 8开始,您可以在没有Guava的情况下完成这项操作。由于Iterable不直接提供流,需要使用StreamSupport,因此有点笨重,但它不需要像问题中的代码那样创建新集合。

private static <T> Iterable<T> concat(Iterable<? extends Iterable<T>> foo) {
    return () -> StreamSupport.stream(foo.spliterator(), false)
        .flatMap(i -> StreamSupport.stream(i.spliterator(), false))
        .iterator();
}

不符合我的需求,因为我的迭代器是惰性的,但是当将这样的迭代器转换为流时,所有的惰性都会丢失。 - odiszapc
@odiszapc 我不确定你的意思。 Iterable.spliterator() 只返回包装了 Iterable.iterator() 的 Spliterator;在流的终端操作开始之前不会抽取任何元素。同样,明确记录 StreamSupport.stream 不会开始查询 spliterator 直到终端操作开始。所以我想你是说你的顶层_iterable_是惰性的,并且你想延迟调用 iterator() 吗?在这种情况下,请使用 StreamSupport.stream 重载,该重载采用一个供应商(StreamSupport.stream(() -> foo.spliterator(), false))。 - Jeffrey Bosboom
2
如果您有时间制作一个最小的示例来重现为什么这个答案不起作用,我会很感兴趣,因为我会从中学到东西。@odiszapc - Jeffrey Bosboom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接