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.
This question is related to
java
collections
iterable
I came across a similar situation while trying to fetch a List
of Project
s, 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.
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
.
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());
}
}
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.
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;
}
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());
}
Two remarks
I use FluentIterable.from(myIterable).toList()
a lot.
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.
Since RxJava is a hammer and this kinda looks like a nail, you can do
Observable.from(iterable).toList().toBlocking().single();
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.
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.
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);
}
}
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.
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);
Try StickyList
from Cactoos:
List<String> list = new StickyList<>(iterable);
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);
I didn't see a simple one line solution without any dependencies. I simple use
List<Users> list;
Iterable<IterableUsers> users = getUsers();
// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());
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.
Source: Stackoverflow.com