在我的应用程序中,我使用3rd party库(确切地说是MongoDB的Spring数据)。
该库的方法返回Iterable<T>
,而我的其他代码则期望Collection<T>
。
是否有任何实用程序方法可以让我快速将一个转换为另一个?我想避免foreach
在代码中创建这么简单的一堆循环。
在我的应用程序中,我使用3rd party库(确切地说是MongoDB的Spring数据)。
该库的方法返回Iterable<T>
,而我的其他代码则期望Collection<T>
。
是否有任何实用程序方法可以让我快速将一个转换为另一个?我想避免foreach
在代码中创建这么简单的一堆循环。
Answers:
使用Guava可以使用Lists.newArrayList(Iterable)或Sets.newHashSet(Iterable)以及其他类似方法。当然,这会将所有元素复制到内存中。如果这是不可接受的,我认为您应该使用适合这些的代码,Iterable
而不是Collection
。番石榴还碰巧提供了方便的方法来完成您可以Collection
使用进行的操作Iterable
(例如Iterables.isEmpty(Iterable)
或Iterables.contains(Iterable, Object)
),但是对性能的影响更为明显。
Lists.newArrayList(Iterable).clear()
线性还是恒定时间操作?
Collection
不能针对Iterable
或不能实现视图的大多数方法来实现,所以这样做对我没有多大意义。
Iterables.concat()
但这会给出一个Iterable
,而不是一个Collection
:(
Iterables.concat()
。更长的答案给出了Collection
...我想知道为什么不更普遍地支持它?
在JDK 8+中,不使用任何其他库:
Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);
编辑:上面的是Iterator
。如果您正在处理Iterable
,
iterable.forEach(target::add);
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;
}
Iterable
考虑的Collection
是从到,我宁愿使用这种方法而不是导入大型的3rd party collections-library。
使用Java 8的简洁解决方案java.util.stream
:
public static <T> List<T> toList(final Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false)
.collect(Collectors.toList());
}
IteratorUtils
于commons-collections
IteratorUtils.toList()
以Java 5之前的方式使用迭代器将元素一个接一个地添加到新创建的列表中。简单,可能最快,但是会增加734 kB的二进制文件,如果您发现这种方法是最好的,则可以自己完成。
IteratorUtils
从commons-collections
5月份的帮助(虽然他们不支持最新的稳定版本3.2.1仿制药):
@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());
版本4.0(目前位于SNAPSHOT中)支持泛型,您可以摆脱@SuppressWarnings
。
更新:IterableAsList
从Cactoos检查。
IterableUtils.toList(Iterable)
,这是一种方便的方法,可以IteratorUtils
在后台使用,但它也是null安全的(与不同IteratorUtils.toList
)。
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());
}
}
Lists.newArrayList(someIterable).clear()
线性还是恒定时间操作?
addAll
,顾名思义,它一个接一个地复制迭代器值。它创建一个副本而不是一个视图。
CollectionUtils
跳过额外的一行来创建集合。
在进行此操作时,请不要忘记所有集合都是有限的,而Iterable没有任何承诺。如果某些东西是可迭代的,那么您可以获取一个迭代器。
for (piece : sthIterable){
..........
}
将扩展为:
Iterator it = sthIterable.iterator();
while (it.hasNext()){
piece = it.next();
..........
}
it.hasNext()不需要返回false。因此,在一般情况下,您不能期望能够将每个Iterable都转换为Collection。例如,您可以遍历所有正自然数,遍历具有循环的事物,一次又一次地产生相同的结果,等等。
否则:Atrey的答案很好。
这不是您问题的答案,但我相信这是您问题的解决方案。该接口org.springframework.data.repository.CrudRepository
确实具有返回的方法,java.lang.Iterable
但您不应使用此接口。在您的情况下,请改用子接口org.springframework.data.mongodb.repository.MongoRepository
。此接口具有返回类型为对象的方法java.util.List
。
如果可用,我将使用自定义实用程序强制转换现有的Collection。
主要:
public static <T> Collection<T> toCollection(Iterable<T> iterable) {
if (iterable instanceof Collection) {
return (Collection<T>) iterable;
} else {
return Lists.newArrayList(iterable);
}
}
理想情况下,上面将使用ImmutableList,但是ImmutableCollection不允许使用可能会提供不良结果的null。
测试:
@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));
}
我为Collections的所有子类型(Set,List等)实现了类似的实用程序。我认为这些已经是番石榴的一部分,但是我还没有找到。
只要你打电话contains
,containsAll
,equals
,hashCode
,remove
,retainAll
,size
或者toArray
,你无论如何都要穿越元素。
如果您偶尔只调用诸如isEmpty
或clear
我想这样的方法,则最好通过延迟创建集合来更好。例如,您可能有一个支持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);
}
}
两句话
我碰到过类似的情况来试图获取一个List
的Project
S,而不是默认Iterable<T> findAll()
的声明CrudRepository
接口。因此,在我的ProjectRepository
接口(从扩展到CrudRepository
)中,我只声明了该findAll()
方法返回List<Project>
而不是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();
}
我认为,这是最简单的解决方案,不需要转换逻辑或使用外部库。
您可以使用Eclipse Collections工厂:
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);
您还可以将转换为Iterable
,LazyIterable
然后使用转换器方法或任何其他可用的API。
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();
以上所有Mutable
类型都扩展java.util.Collection
。
注意:我是Eclipse Collections的提交者。