489

In my application I use 3rd party library (Spring Data for MongoDB to be exact).

Methods of this library return Iterable<T>, while the rest of my code expects Collection<T>.

Is there any utility method somewhere that will let me quickly convert one to the other? I would like to avoid creating a bunch of foreach loops in my code for such a simple thing.

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
Ula Krukar
  • 12,549
  • 20
  • 51
  • 65
  • 4
    Any utiliy method for performing the operation is bound to iterate of the collection anyway, so you can't expect any performance gain. But if you're just looking for syntactic sugar I would go for Guava or perhaps Apache Collections. – Sebastian Ganslandt Jun 20 '11 at 20:12
  • "*is bound to iterate of the collection anyway*", -- no, it's not. See my answer for details. – aioobe Jun 20 '11 at 20:31
  • 4
    in your specific usecase, you could just extend CrudRepository with your own interface with methods that return Collection / List / Set (as needed) instead of Iterable – Kevin Van Dyck Feb 18 '18 at 15:37

21 Answers21

415

In JDK 8+, without using any additional libs:

Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);

Edit: The above one is for Iterator. If you are dealing with Iterable,

iterable.forEach(target::add);
Thamme Gowda
  • 11,249
  • 5
  • 50
  • 57
406

With Guava you can use Lists.newArrayList(Iterable) or Sets.newHashSet(Iterable), among other similar methods. This will of course copy all the elements in to memory. If that isn't acceptable, I think your code that works with these ought to take Iterable rather than Collection. Guava also happens to provide convenient methods for doing things you can do on a Collection using an Iterable (such as Iterables.isEmpty(Iterable) or Iterables.contains(Iterable, Object)), but the performance implications are more obvious.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
ColinD
  • 108,630
  • 30
  • 201
  • 202
  • 1
    Does it iterate through all elements directly? I.e., is `Lists.newArrayList(Iterable).clear()` a linear or constant time operation? – aioobe Jun 20 '11 at 20:13
  • 2
    @aioobe: It creates a copy of the iterable. It wasn't specified that a view was desired, and given that most of the methods on `Collection` either can't be implemented for a view of an `Iterable` or won't be efficient, it doesn't make much sense to me to do that. – ColinD Jun 20 '11 at 20:23
  • @ColinD what if I want a view ? Actually, what I want is a Collection view that is the result of appending a source Collection with another element. I can use `Iterables.concat()` but that gives an `Iterable`, not a `Collection` :( – Hendy Irawan May 10 '14 at 11:53
  • 1
    This is my question: http://stackoverflow.com/questions/4896662/combine-multiple-collections-into-a-single-logical-collection . Unfortunately the simple answer that doesn't solve the problem is by using `Iterables.concat()`. The much longer answer gives `Collection` ... I wonder why this isn't more commonly supported? – Hendy Irawan May 10 '14 at 11:55
109

Concise solution with Java 8 using java.util.stream:

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}

Since Java 16, you can use Stream.toList():

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .toList();
}
xehpuk
  • 7,814
  • 3
  • 30
  • 54
  • 1
    this approach is way too slow compared to `IteratorUtils` from `commons-collections` – Alex Burdusel Aug 26 '16 at 13:59
  • 3
    How much slower? `IteratorUtils.toList()` uses the iterator in a pre Java 5 fashion to add the elements one by one to a newly created list. Simple and possibly fastest, but adds 734 kB to your binary and you could do this on your own if you found this method to be the best. – xehpuk Aug 26 '16 at 14:44
  • 10
    I've done a primitive benchmark concluding that sometimes the first is faster, sometimes the second is faster. Show us your benchmark. – xehpuk Aug 26 '16 at 15:05
  • 3
    this prob could be new accepted answer - It's nice to avoid excess libs (like Guava). – java-addict301 Oct 25 '19 at 18:33
101

You may write your own utility method for this as well:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
Atreys
  • 3,741
  • 1
  • 17
  • 27
  • 34
    +1 If going from `Iterable` to `Collection` is the only concern, I'd prefer this approach over importing a large 3rd party collections-library. – aioobe Jun 20 '11 at 20:25
  • 2
    4 lines of function code is much more preferable over 2 MB of compiled library code for which 99% of it goes unused. There's another cost: licensing complications. The Apache 2.0 license is flexible, but not without some tedious mandates. Ideally we would see _some_ of these common patterns integrated directly into the Java runtime libraries. – Coder Guy Nov 03 '15 at 20:01
  • 2
    One more point, since you're using an ArrayList anyhow, why not simply go with the covariant List type instead? This allows you to satisfy more contracts without down-casting or recomposing and Java has no support for lower type bounds anyway. – Coder Guy Nov 03 '15 at 20:12
  • @JonathanNeufeld or why not just go ahead and return an ArrayList? – Juan Dec 06 '15 at 07:55
  • 5
    @Juan Because that isn't very [SOLID](https://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29). An ArrayList exposes implementation details that are most likely unnecessary (YAGNI), which violates the single responsibility and dependency inversion principles. I would leave it at List because it does expose a little more than Collection does while remaining completely SOLID. If you're worried about the JVM performance impact of opcode INVOKEINTERFACE over INVOKEVIRTUAL, plenty of benchmarks will reveal that it's not worth losing sleep over. – Coder Guy Dec 07 '15 at 08:57
  • Why not `LinkedList`? `ArrayList` is inefficient if the size of an array isn't defined. `LinkedList` add method complexity is O(1) whereas`ArrayList` add method complexity is O(N) in the worst scenario. `ArrayList` in order to grow copies all the previous array and makes new `ArrayList` with doubled size. – Michał Stochmal Nov 15 '18 at 13:04
53

IteratorUtils from commons-collections may help (although they don't support generics in the latest stable version 3.2.1):

@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());

Version 4.0 (which is in SNAPSHOT at this moment) supports generics and you can get rid of the @SuppressWarnings.

Update: Check IterableAsList from Cactoos.

yegor256
  • 102,010
  • 123
  • 446
  • 597
  • 5
    Since 4.1 there is also `IterableUtils.toList(Iterable)`, which is a convenience method and uses `IteratorUtils` under the hood, but also is null-safe (unlike `IteratorUtils.toList`). – Yoory N. Dec 07 '17 at 10:53
37

When you get your Iterable from Spring Data you have a couple of additional alternatives.

  1. You can override the method that returns the Iterable in the repository with a version that returns a List, Set or Streamable. This way Spring Data is doing the conversion for you.

  2. You may do so in a super interface of your repositories so you don't have to repeat the override in all your repository interfaces.

  3. If you happen to use Spring Data JPA this is already done for you in JpaRepository

  4. You may do the conversion using the just mentioned Streamable yourself:

    Iterable<X> iterable = repo.findAll();
    List<X> list = Streamable.of(iterable).toList();
    

And since you mention being upset, maybe a little background for the decision to use Iterable help as well.

  1. It is expected that it is actually fairly rare to actually require a Collection so in many cases it shouldn't make a difference.
  2. Using the overriding mechanics one can return different types which wouldn't be possible with a more specific return type like Collection. This would make it impossible to return a Streamable which is intended for cases where a store may decide to return a result before all elements have been fetched.
  3. Streamable would actually be a flexible return type, since it offers easy conversions to List, Set, Stream and is itself an Iterable. But this would require you to use a Spring Data specific type in your application which many users wouldn't like.

There is a section about this in the reference documentation.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
25

From CollectionUtils:

List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())

Here are the full sources of this utility method:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
16

I came across a similar situation while trying to fetch a List of Projects, rather than the default Iterable<T> findAll() declared in CrudRepository interface. So, in my ProjectRepository interface (which extends from CrudRepository), I simply declared the findAll() method to return a List<Project> instead of Iterable<Project>.

package com.example.projectmanagement.dao;

import com.example.projectmanagement.entities.Project;
import org.springframework.data.repository.CrudRepository;
import java.util.List;

public interface ProjectRepository extends CrudRepository<Project, Long> {

    @Override
    List<Project> findAll();
}

This is the simplest solution, I think, without requiring conversion logic or usage of external libraries.

Manish Giri
  • 3,562
  • 8
  • 45
  • 81
15

I use FluentIterable.from(myIterable).toList() a lot.

fringd
  • 2,380
  • 1
  • 18
  • 13
14

While at it, do not forget that all collections are finite, while Iterable has no promises whatsoever. If something is Iterable you can get an Iterator and that is it.

for (piece : sthIterable){
..........
}

will be expanded to:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}

it.hasNext() is not required to ever return false. Thus in the general case you cannot expect to be able to convert every Iterable to a Collection. For example you can iterate over all positive natural numbers, iterate over something with cycles in it that produces the same results over and over again, etc.

Otherwise: Atrey's answer is quite fine.

An̲̳̳drew
  • 13,375
  • 13
  • 47
  • 46
  • 1
    Has anyone ever actually come across an Iterable that iterates over something infinite (like the natural numbers example given in the answer), in practice / real code? I would think that such an Iterable would cause pain and woes in a lot of places... :) – David May 06 '14 at 12:56
  • 2
    @David Although I can't specifically point to an infinite Iterator in any of my production code, I can think of cases where they might occur. A video game might have a skill that creates items in the cyclical pattern that the above answer suggests. While I haven't encountered any infinite iterators, I have definitely encountered iterators where memory is a real concern. I have iterators over the files on disk. If I have a full 1TB disk and 4GB of ram, I could easily run out of memory converting my iterator to a Collection. – radicaledward101 Feb 27 '15 at 15:07
9

This is not an answer to your question but I believe it is the solution to your problem. The interface org.springframework.data.repository.CrudRepository does indeed have methods that return java.lang.Iterable but you should not use this interface. Instead use sub interfaces, in your case org.springframework.data.mongodb.repository.MongoRepository. This interface has methods that return objects of type java.util.List.

Ludwig Magnusson
  • 13,964
  • 10
  • 38
  • 53
  • 3
    I would promote using the generic CrudRepository to avoid binding your code to a concrete implementation. – stanlick Nov 20 '15 at 15:08
8

I use my custom utility to cast an existing Collection if available.

Main:

public static <T> Collection<T> toCollection(Iterable<T> iterable) {
    if (iterable instanceof Collection) {
        return (Collection<T>) iterable;
    } else {
        return Lists.newArrayList(iterable);
    }
}

Ideally the above would use ImmutableList, but ImmutableCollection does not allow nulls which may provide undesirable results.

Tests:

@Test
public void testToCollectionAlreadyCollection() {
    ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST);
    assertSame("no need to change, just cast", list, toCollection(list));
}

@Test
public void testIterableToCollection() {
    final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST);

    Collection<String> collection = toCollection(new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return expected.iterator();
        }
    });
    assertNotSame("a new list must have been created", expected, collection);
    assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection));
}

I implement similar utilities for all subtypes of Collections (Set,List,etc). I'd think these would already be part of Guava, but I haven't found it.

Aaron Roller
  • 1,074
  • 1
  • 14
  • 19
  • 1
    Your year-old answer is the basis of a new question http://stackoverflow.com/questions/32570534/are-there-any-java-standard-classes-that-implement-iterable-without-implementing attracting lots of views and comments. – Paul Boddington Sep 15 '15 at 01:00
6

In Java 8 you can do this to add all elements from an Iterable to Collection and return it:

public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
  Collection<T> collection = new ArrayList<>();
  iterable.forEach(collection::add);
  return collection;
}

Inspired by @Afreys answer.

6

Since RxJava is a hammer and this kinda looks like a nail, you can do

Observable.from(iterable).toList().toBlocking().single();
DariusL
  • 4,007
  • 5
  • 32
  • 44
6

As soon as you call contains, containsAll, equals, hashCode, remove, retainAll, size or toArray, you'd have to traverse the elements anyway.

If you're occasionally only calling methods such as isEmpty or clear I suppose you'd be better of by creating the collection lazily. You could for instance have a backing ArrayList for storing previously iterated elements.

I don't know of any such class in any library, but it should be a fairly simple exercise to write up.

aioobe
  • 413,195
  • 112
  • 811
  • 826
4

Here's an SSCCE for a great way to do this in Java 8

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class IterableToCollection {
    public interface CollectionFactory <T, U extends Collection<T>> {
        U createCollection();
    }

    public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) {
        U collection = factory.createCollection();
        iterable.forEach(collection::add);
        return collection;
    }

    public static void main(String[] args) {
        Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
        ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
        HashSet<Integer> hashSet = collect(iterable, HashSet::new);
        LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
    }
}
michaelsnowden
  • 6,031
  • 2
  • 38
  • 83
3

Two remarks

  1. There is no need to convert Iterable to Collection to use foreach loop - Iterable may be used in such loop directly, there is no syntactical difference, so I hardly understand why the original question was asked at all.
  2. Suggested way to convert Iterable to Collection is unsafe (the same relates to CollectionUtils) - there is no guarantee that subsequent calls to the next() method return different object instances. Moreover, this concern is not pure theoretical. E.g. Iterable implementation used to pass values to a reduce method of Hadoop Reducer always returns the same value instance, just with different field values. So if you apply makeCollection from above (or CollectionUtils.addAll(Iterator)) you will end up with a collection with all identical elements.
al0
  • 97
  • 3
1

Try StickyList from Cactoos:

List<String> list = new StickyList<>(iterable);
yegor256
  • 102,010
  • 123
  • 446
  • 597
1

Kinda late to the party, but I created a very elegant Java 8 solution that allows converting an Iterable of T to any Collection of T, without any libraries:

public static <T, C extends Collection<T>> C toCollection(Iterable<T> iterable, Supplier<C> baseSupplier) 
{
    C collection = baseSupplier.get();
    
    iterable.forEach(collection::add);
    
    return collection;
}

Usage Example:

Iterable<String> iterable = ...;
List<String> list = toCollection(iterable, ArrayList::new);
David
  • 114
  • 1
  • 7
0

You can use Eclipse Collections factories:

Iterable<String> iterable = Arrays.asList("1", "2", "3");

MutableList<String> list = Lists.mutable.withAll(iterable);
MutableSet<String> set = Sets.mutable.withAll(iterable);
MutableSortedSet<String> sortedSet = SortedSets.mutable.withAll(iterable);
MutableBag<String> bag = Bags.mutable.withAll(iterable);
MutableSortedBag<String> sortedBag = SortedBags.mutable.withAll(iterable);

You can also convert the Iterable to a LazyIterable and use the converter methods or any of the other available APIs available.

Iterable<String> iterable = Arrays.asList("1", "2", "3");
LazyIterable<String> lazy = LazyIterate.adapt(iterable);

MutableList<String> list = lazy.toList();
MutableSet<String> set = lazy.toSet();
MutableSortedSet<String> sortedSet = lazy.toSortedSet();
MutableBag<String> bag = lazy.toBag();
MutableSortedBag<String> sortedBag = lazy.toSortedBag();

All of the above Mutable types extend java.util.Collection.

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44
0

If you could update to Spring Data 3, this has been addressed there. There is a new interface ListCrudRepository which do exactly what you want.

Here is the interface from https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0:

public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
    <S extends T> List<S> saveAll(Iterable<S> entities);
    List<T> findAll();
    List<T> findAllById(Iterable<ID> ids);
}

Note in version 3 you must implement two interfaces

So in version 2:

public interface PersonRepository<Person, Long> extends 
   PagingAndSortingRepository<Person, Long> {}

In version 3 should be changed to:

public interface PersonRepository<Person, Long> extends
    PagingAndSortingRepository<Person, Long>,ListCrudRepository<Person, Long> {}

Other changes are mentioned in https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0

Alireza Fattahi
  • 42,517
  • 14
  • 123
  • 173