41

Is there a flatten method in Guava - or an easy way to convert an Iterable<Iterable<T>> to an Iterable<T>?

I have a Multimap<K, V> [sourceMultimap] and I want to return all values where the key matches some predicate [keyPredicate]. So at the moment I have:

Iterable<Collection<V>> vals = Maps.filterKeys(sourceMultimap.asMap(), keyPredicate).values();

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

I've looked through the Guava docs, but nothing jumped out. I am just checking I've not missed anything. Otherwise, I'll extract my three lines into a short flatten generic method and leave it as that.

joragupra
  • 692
  • 1
  • 12
  • 23
Andy Whitfield
  • 2,373
  • 2
  • 19
  • 22

2 Answers2

74

The Iterables.concat method satisfies that requirement:

public static <T> Iterable<T> concat(Iterable<? extends Iterable<? extends T>> inputs)
Pang
  • 9,564
  • 146
  • 81
  • 122
Sean Parsons
  • 2,832
  • 21
  • 17
  • 6
    I would guess this is because this is only one level concatenation, not really flatten:) – Gabriel Ščerbák May 10 '11 at 17:05
  • Also if a collection is being generated from a guava transform, you can use 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 Dec 08 '14 at 18:51
4

As of Java 8, you can do this without Guava. It's a bit clunky because Iterable doesn't directly provide streams, requiring the use of StreamSupport, but it doesn't require creating a new collection like the code in the question.

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();
}
Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
  • Does not fit my needs, because my iterator is lazy, but all lazyness is lost when convert such an iterator to stream. – odiszapc Feb 27 '16 at 16:40
  • @odiszapc I'm not sure what you mean. Iterable.spliterator() just returns a Spliterator wrapping Iterable.iterator(); no elements are drawn until the stream's terminal operation begins. Similarly, StreamSupport.stream is explicitly documented not to begin querying the spliterator until the terminal operation begins. So I think you're saying your top-level _iterable_ is lazy, and you want to delay calling iterator() on it? In that case, use the StreamSupport.stream overload taking a supplier (`StreamSupport.stream(() -> foo.spliterator(), false)`). – Jeffrey Bosboom Feb 27 '16 at 21:54
  • 2
    @odiszapc Otherwise, I'd be interested in a minimal example reproducing why this answer doesn't work, if you have time to make one, because I'll learn something from it. – Jeffrey Bosboom Feb 27 '16 at 21:54