Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy way to convert Iterable to Collection

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.

like image 837
Ula Krukar Avatar asked Jun 20 '11 19:06

Ula Krukar


People also ask

How do I convert Iterable to collections?

To convert iterable to Collection, the iterable is first converted into spliterator. Then with the help of StreamSupport. stream(), the spliterator can be traversed and then collected with the help collect() into collection.

How do you convert a list to Iterable in darts?

fromIterable() to convert the list to a map. In the method for the first argument, we pass myList as an iterable. The method then calculates the key and value for each element of the iterable.

Does Collection implement Iterable?

The Collection interface extends Iterable , so all subtypes of Collection also implement the Iterable interface. For instance, both the Java List and Set interfaces extend the Collection interface, and thereby also the Iterable interface.

Should I use Iterable or list?

Personally I would use Iterable<T> if that allows you to do everything you want it to. It's more flexible for callers, and in particular it lets you do relatively easy filtering/projection/etc using the Google Java Collections (and no doubt similar libraries).


13 Answers

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.

like image 58
ColinD Avatar answered Sep 24 '22 02:09

ColinD


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); 
like image 32
Thamme Gowda Avatar answered Sep 24 '22 02:09

Thamme Gowda


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;
}
like image 28
Atreys Avatar answered Sep 24 '22 02:09

Atreys


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());
}
like image 37
xehpuk Avatar answered Sep 24 '22 02:09

xehpuk


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.

like image 45
yegor256 Avatar answered Sep 24 '22 02:09

yegor256


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());
    }
}
like image 32
Tomasz Nurkiewicz Avatar answered Sep 25 '22 02:09

Tomasz Nurkiewicz


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.

like image 21
Jens Schauder Avatar answered Sep 21 '22 02:09

Jens Schauder


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

like image 43
fringd Avatar answered Sep 24 '22 02:09

fringd


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.

like image 34
Alexander Shopov Avatar answered Sep 25 '22 02:09

Alexander Shopov


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.

like image 39
Manish Giri Avatar answered Sep 21 '22 02:09

Manish Giri


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.

like image 41
Ludwig Magnusson Avatar answered Sep 23 '22 02:09

Ludwig Magnusson


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.

like image 37
Aaron Roller Avatar answered Sep 25 '22 02:09

Aaron Roller


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.

like image 36
aioobe Avatar answered Sep 21 '22 02:09

aioobe