Spring Data for MongoDB
)。该库的方法返回
Iterable<T>
,而我的其他代码期望Collection<T>
。是否有任何实用方法可以让我快速地将一个转换为另一个?我希望避免在我的代码中创建大量的
foreach
循环来处理这么简单的事情。Spring Data for MongoDB
)。Iterable<T>
,而我的其他代码期望Collection<T>
。foreach
循环来处理这么简单的事情。JDK 8及以上版本,不使用任何额外的库:
Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);
编辑:上面的内容是针对Iterator
。如果您要处理Iterable
,
iterable.forEach(target::add);
iterable.forEach(target::add);
的意思是对可迭代对象中的每个元素都执行 target.add()
方法。 - Cephalopod使用Guava,您可以使用Lists.newArrayList(Iterable)或Sets.newHashSet(Iterable)等类似方法。当然,这将复制所有元素到内存中。如果不接受这种情况,我认为与这些一起工作的代码应该使用Iterable
而不是Collection
。 Guava还提供了方便的方法来执行可以使用Iterable
执行的Collection
上的操作(例如Iterables.isEmpty(Iterable)
或Iterables.contains(Iterable, Object)
),但性能影响更为明显。
Lists.newArrayList(Iterable).clear()
是线性时间还是常数时间操作? - aioobeCollection
上的方法不能为Iterable
的视图实现或不太高效,我认为这样做没有多少意义。 - ColinDIterables.concat()
,但它只提供了一个Iterable
,而不是一个Collection
:( - Hendy Irawan使用Java 8中的java.util.stream
提供简洁的解决方案:
public static <T> List<T> toList(final Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false)
.collect(Collectors.toList());
}
自Java 16开始,您可以使用Stream.toList()
:
public static <T> List<T> toList(final Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false)
.toList();
}
commons-collections
中的IteratorUtils
相比,这种方法太慢了。 - Alex BurduselIteratorUtils.toList()
以类似Java 5之前的方式使用迭代器,逐个将元素添加到新创建的列表中。这种方法简单且可能是最快的,但会使二进制文件增加734 kB大小,如果你认为这种方法是最好的,你可以自己实现它。 - xehpukpublic static <E> Collection<E> makeCollection(Iterable<E> iter) {
Collection<E> list = new ArrayList<E>();
for (E item : iter) {
list.add(item);
}
return list;
}
LinkedList
?如果数组的大小未定义,ArrayList
是低效的。LinkedList
的添加方法复杂度为O(1),而ArrayList
的添加方法复杂度在最坏情况下为O(N)。为了增长,ArrayList
会复制所有先前的数组,并创建一个两倍大小的新ArrayList
。 - Michał StochmalIteratorUtils
可能会有所帮助(尽管它们在最新的稳定版本3.2.1中不支持泛型)来自 commons-collections
:
@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());
目前处于SNAPSHOT版本的版本4.0已经支持泛型,您可以摆脱@SuppressWarnings
。
更新:请查看来自Cactoos的IterableAsList
。
IterableUtils.toList(Iterable)
方法,它是一个便捷方法,底层使用了IteratorUtils
,但与IteratorUtils.toList
不同的是,它也具有空值安全性。 - Yoory N.Streamable
版本。这样Spring Data就会为你完成转换。
2. 你可以在你的仓库超级接口中实现此操作,这样你就不必在所有的仓库接口中都进行重写。
3. 如果你使用Spring Data JPA,那么这个过程已经在`JpaRepository`中为你完成了。
4. 你也可以使用刚才提到的`Streamable`自己进行转换:Iterable<X> iterable = repo.findAll();
List<X> list = Streamable.of(iterable).toList();
既然您提到感到不满,也许为决定使用Iterable
提供一些背景信息可能会有所帮助。
Collection
,因此在许多情况下,这不应该产生影响。Collection
这样更具体的返回类型中是不可能的。这将使得无法返回Streamable
,而Streamable
适用于存储在获取所有元素之前可能返回结果的情况。Streamable
实际上将是一个灵活的返回类型,因为它提供了易于转换为List
,Set
,Stream
并且本身就是一个Iterable
。但是,这将要求您在应用程序中使用Spring Data特定类型,这可能不受许多用户的欢迎。参考文档中有关于此的章节说明。
来自 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());
}
}
Lists.newArrayList(someIterable).clear()
是线性时间还是常数时间操作? - aioobeaddAll
的源代码,顾名思义,它会将迭代器的值一个接一个地复制;它创建的是副本而不是视图。 - Tomasz NurkiewiczCollectionUtils
中没有一种方法可以跳过在额外的行中创建集合。 - Kalle RichterProject
列表时遇到了类似的情况,而不是在CrudRepository
接口中声明的默认Iterable<T> findAll()
。因此,在我的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();
}
我认为这是最简单的解决方案,而不需要转换逻辑或使用外部库。
我经常使用FluentIterable.from(myIterable).toList()
。
在此期间,不要忘记所有集合都是有限的,而Iterable则没有任何承诺。如果某个东西是Iterable,那么你可以获取一个Iterator,就这样。
for (piece : sthIterable){
..........
}
将会被扩展为:
Iterator it = sthIterable.iterator();
while (it.hasNext()){
piece = it.next();
..........
}
it.hasNext()不一定会返回false。因此,在一般情况下,您不能指望能够将每个Iterable转换为Collection。例如,您可以迭代所有正自然数、迭代在其中产生重复结果的具有循环的内容等。
否则:Atrey的答案非常好。