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

这个库的方法返回Iterable<T>,而我的其余代码期望Collection<T>。

有没有什么实用的方法可以让我快速地把一个转换成另一个?为了这么简单的事情,我希望避免在代码中创建一堆foreach循环。


当前回答

从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());
    }
}

其他回答

当你从Spring Data中获得你的Iterable时,你有两个额外的选择。

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. 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. If you happen to use Spring Data JPA this is already done for you in JpaRepository You may do the conversion using the just mentioned Streamable yourself: Iterable<X> iterable = repo.findAll(); List<X> list = Streamable.of(iterable).toList();

既然你提到了沮丧,也许还有一些决定使用Iterable帮助的背景。

It is expected that it is actually fairly rare to actually require a Collection so in many cases it shouldn't make a difference. 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. 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.

在参考文档中有关于这方面的章节。

来自common -collections的IteratorUtils可能会有所帮助(尽管它们在最新的稳定版本3.2.1中不支持泛型):

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

4.0版本(目前在SNAPSHOT中)支持泛型,您可以去掉@SuppressWarnings。

更新:检查IterableAsList从仙人掌。

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

如果您可以更新到Spring Data 3,那么这个问题已经解决了。有一个新的界面ListCrudRepository,它做的正是你想要的。

这是来自https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0:的界面

public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
    <S extends T> List<S> saveAll(Iterable<S> entities);
    List<T> findAll();
    List<T> findAllById(Iterable<ID> ids);
}

注意,在版本3中必须实现两个接口

所以在版本2中

public interface PersonRepository<Person, Long> extends 
   PagingAndSortingRepository<Person, Long> {}

在版本3中应改为:

public interface PersonRepository<Person, Long> extends
    PagingAndSortingRepository<Person, Long>,ListCrudRepository<Person, Long> {}

其他变化见https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0

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

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

将扩大到:

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

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

除此之外:Atrey的回答很好。