467

在我的应用程序中,我使用 3rd 方库(确切地说是用于 MongoDB 的 Spring Data)。

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

是否有任何实用方法可以让我快速将一种转换为另一种?我想避免foreach在我的代码中为这么简单的事情创建一堆循环。

4

22 回答 22

399

使用Guava,您可以使用Lists.newArrayList(Iterable)Sets.newHashSet(Iterable)以及其他类似方法。这当然会将所有元素复制到内存中。如果这是不可接受的,我认为你的代码应该使用这些Iterable而不是Collection. Guava 也恰好提供了方便的方法来做你可以在Collectionusing an Iterable(例如Iterables.isEmpty(Iterable)or Iterables.contains(Iterable, Object))上做的事情,但对性能的影响更明显。

于 2011-06-20T20:05:18.597 回答
397

在 JDK 8+ 中,不使用任何额外的库:

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

编辑:以上是针对Iterator. 如果你正在处理Iterable

iterable.forEach(target::add);
于 2015-05-27T06:05:46.810 回答
101

您也可以为此编写自己的实用程序方法:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}
于 2011-06-20T20:14:55.863 回答
98

使用 Java 8 的简洁解决方案java.util.stream

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}
于 2014-06-02T14:25:40.050 回答
50

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

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

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

更新:IterableAsListCactoos检查。

于 2013-04-28T14:12:25.110 回答
24

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());
    }
}
于 2011-06-20T20:07:29.543 回答
18

当您Iterable从 Spring Data 获取您的数据时,您有几个额外的选择。

  1. 您可以使用返回或的版本覆盖Iterable存储库中返回List的方法。这样,Spring Data 就会为您进行转换。SetStreamable

  2. 您可以在存储库的超级界面中执行此操作,因此您不必在所有存储库接口中重复覆盖。

  3. 如果您碰巧使用 Spring Data JPA,这已经为您完成了JpaRepository

  4. 您可以使用刚才提到的Streamable自己进行转换:

    Iterable<X> iterable = repo.findAll();
    List<X> list = Streamable.of(iterable).toList();
    

既然你提到了心烦意乱,也许还有一点背景来决定使用Iterable帮助。

  1. 预计实际上需要 a 实际上是相当罕见的,Collection因此在许多情况下它不应该有所作为。
  2. 使用覆盖机制可以返回不同的类型,而使用更具体的返回类型(如Collection. 这将使得不可能返回Streamable用于商店可能决定在获取所有元素之前返回结果的情况。
  3. Streamable实际上是一个灵活的返回类型,因为它提供了到,的简单转换List,并且它本身就是一个. 但这将要求您在应用程序中使用许多用户不喜欢的 Spring Data 特定类型。SetStreamIterable

参考文档中有一个关于此的部分

于 2021-05-06T07:06:32.417 回答
14

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

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

将扩展为:

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

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

否则:Atrey 的回答很好。

于 2012-08-30T06:50:41.300 回答
14

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

于 2013-10-18T23:11:21.490 回答
14

我在尝试获取 a Listof s 时遇到了类似的情况,而不是在接口中声明Project的默认值。因此,在我的接口(从 扩展)中,我只是声明了返回 a而不是.Iterable<T> findAll()CrudRepositoryProjectRepositoryCrudRepositoryfindAll()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();
}

我认为这是最简单的解决方案,不需要转换逻辑或使用外部库。

于 2020-05-30T04:14:53.807 回答
9

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

于 2013-06-18T12:04:08.120 回答
7

如果可用,我使用我的自定义实用程序来转换现有的集合。

主要的:

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

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

测试:

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

我为集合的所有子类型(集合、列表等)实现了类似的实用程序。我认为这些已经是番石榴的一部分,但我还没有找到。

于 2014-09-04T08:35:13.950 回答
6

一旦你调用contains, containsAll, equals, hashCode, remove, retainAll, sizeor toArray,无论如何你都必须遍历元素。

如果您偶尔只调用诸如isEmpty或之类的方法,clear我想您最好懒惰地创建集合。例如,您可以为ArrayList存储先前迭代的元素提供支持。

我不知道任何库中有任何这样的类,但编写起来应该是一个相当简单的练习。

于 2011-06-20T20:04:20.683 回答
6

在 Java 8 中,您可以这样做来添加来自Iterableto的所有元素Collection并返回它:

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

受@Afreys 回答的启发。

于 2014-04-24T14:05:37.620 回答
6

由于 RxJava 是一把锤子,这有点像钉子,你可以这样做

Observable.from(iterable).toList().toBlocking().single();
于 2015-10-15T07:57:52.770 回答
4

这是一个 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);
    }
}
于 2016-08-08T06:54:30.607 回答
3

我没有看到没有任何依赖关系的简单单行解决方案。我简单使用

List<Users> list;
Iterable<IterableUsers> users = getUsers();

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());
于 2020-04-27T07:40:01.337 回答
2

两个备注

  1. 无需将 Iterable 转换为 Collection 即可使用 foreach 循环 - Iterable 可以直接在这样的循环中使用,没有语法差异,所以我几乎不明白为什么要问原始问题。
  2. 将 Iterable 转换为 Collection 的建议方法是不安全的(与 CollectionUtils 相关) - 不能保证对 next() 方法的后续调用返回不同的对象实例。此外,这种担忧也不是纯理论的。例如,用于将值传递给 Hadoop Reducer 的 reduce 方法的 Iterable 实现总是返回相同的值实例,只是具有不同的字段值。因此,如果您从上面应用 makeCollection(或 CollectionUtils.addAll(Iterator)),您最终将得到一个包含所有相同元素的集合。
于 2013-02-20T12:15:37.227 回答
1

StickyListCactoos尝试:

List<String> list = new StickyList<>(iterable);
于 2017-06-20T17:35:49.057 回答
1

参加聚会有点晚了,但我创建了一个非常优雅的 Java 8 解决方案,它允许将 T 的 Iterable 转换为 T 的任何 Collection,而无需任何库:

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

使用示例:

Iterable<String> iterable = ...;
List<String> list = toCollection(iterable, ArrayList::new);
于 2021-02-22T15:31:28.060 回答
0

您可以使用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为 aLazyIterable并使用转换器方法或任何其他可用的 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 的提交者。

于 2020-01-17T04:45:42.490 回答
0

尝试窄铸:(List<iterable_type>)iterable;

于 2022-01-24T05:15:36.403 回答