简单的方法来改变集合的Iterable

在我的应用程序中,我使用第三方库(准确地说是MongoDb的Spring Data)。

这个库的方法返回Iterable<T> ,而我的代码的其余部分需要Collection<T>

有什么实用的方法,让我快速转换到另一个。 我想避免在我的代码中做这样一个简单的事情foreach循环banch。

使用番石榴,您可以使用Lists.newArrayList(Iterable)或Sets.newHashSet(Iterable)等方法。 这当然会把所有元素都复制到内存中。 如果这是不可接受的,我认为你的代码应该采用可Iterable而不是Collection 。 番石榴也碰巧提供了方便的方法来处理你可以在一个Collection使用Iterable (例如Iterables.isEmpty(Iterable)Iterables.contains(Iterable, Object) )做的事情,但是性能的影响更为明显。

在JDK 8中,不依赖于其他库:

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

编辑:上面的是为Iterator 。 如果你正在处理Iterable

 iterable.forEach(target::add); 

你也可以写下你自己的实用方法:

 public static <E> Collection<E> makeCollection(Iterable<E> iter) { Collection<E> list = new ArrayList<E>(); for (E item : iter) { list.add(item); } return list; } 

使用java.util.stream Java 8的简明解决scheme:

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

来自commons-collections IteratorUtils可能会有帮助(尽pipe它们不支持最新的稳定版本3.2.1中的generics):

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

4.0版(目前在SNAPSHOT中)支持generics,您可以摆脱@SuppressWarnings

更新:从Cactoos检查IterableAsList

来自CollectionUtils :

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

以下是此实用程序方法的完整来源:

 public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) { while (iterator.hasNext()) { collection.add(iterator.next()); } } 

尽pipe如此,不要忘记,所有的集合都是有限的,而Iterable没有任何承诺。 如果某些东西是可迭代的,你可以得到一个迭代器,就是这样。

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

将被扩展到:

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

it.hasNext()不需要返回false。 因此,在一般情况下,您不能期望能够将每个Iterable转换为一个Collection。 例如,你可以遍历所有正的自然数,迭代循环的东西,一遍又一遍地产生相同的结果,等等。

否则:Atrey的答案相当好。

我使用FluentIterable.from(myIterable).toList()很多。

这不是你的问题的答案,但我相信这是你的问题的解决scheme。 接口org.springframework.data.repository.CrudRepository确实有返回java.lang.Iterable方法,但不应该使用这个接口。 而是使用子接口,在你的情况下org.springframework.data.mongodb.repository.MongoRepository 。 该接口具有返回java.util.Listtypes的对象的方法。

如果可用,我使用自定义实用工具来投射现有集合。

主要:

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

理想情况下,上面将使用ImmutableList,但ImmutableCollection不允许可能提供不良结果的空值。

testing:

 @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)); } 

我为集合的所有子types(Set,List等)实现类似的实用程序。 我认为这些已经是番石榴的一部分,但我还没有find。

只要你调用containscontainsAllequalshashCoderemoveretainAllsizetoArray ,你就不得不遍历元素。

如果偶尔只调用isEmptyclear等方法,我想你会更懒惰地创build集合。 你可以例如有一个支持ArrayList来存储以前迭代的元素。

我不知道任何一个图书馆里有这样的课,但是写起来应该是一个相当简单的练习。

在Java 8中,您可以这样做,将所有元素从Iterable添加到Collection并返回:

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

受@Afreys的启发回答。

既然RxJava是一把锤子,而且这个有点像钉子,你可以做

 Observable.from(iterable).toList().toBlocking().single(); 

这是一个SSCCE,这是一个在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); } } 

两个评论

  1. 不需要将Iterable转换为Collection来使用foreach循环 – Iterable可以直接用在这样的循环中,没有语法上的差异,所以我几乎不明白为什么要问原来的问题。
  2. 将Iterable转换为Collection的build议方法是不安全的(与CollectionUtils相同) – 不保证对next()方法的后续调用返回不同的对象实例。 而且,这个问题不是纯粹的理论。 例如,用于将值传递给Hadoop Reducer的reduce方法的Iterable实现总是返回相同的值实例,只是具有不同的字段值。 所以,如果你从上面应用makeCollection(或者CollectionUtils.addAll(Iterator)),你将会得到一个包含所有相同元素的集合。

从Cactoos尝试StickyList :

 List<String> list = new StickyList<>(iterable);