将Iterable转换为Collection的简便方法

时间:2011-06-20 19:56:45

标签: java collections

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

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

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

21 个答案:

答案 0 :(得分:357)

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

答案 1 :(得分:311)

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

Using reader As IO.StreamReader = IO.File.OpenText("\\10.31.42.249\share\file.txt") ...

编辑:上面的内容适用于Iterator<T> source = ...; List<T> target = new ArrayList<>(); source.forEachRemaining(target::add); 。如果您正在处理Iterator

Iterable

答案 2 :(得分:86)

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

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

答案 3 :(得分:67)

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

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

答案 4 :(得分:44)

来自commons-collections

IteratorUtils可能有所帮助(尽管他们不支持最新稳定版本3.2.1中的泛型):

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

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

更新:从IterableAsList检查Cactoos

答案 5 :(得分:19)

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

答案 6 :(得分:14)

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

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

将扩展为:

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

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

否则:Atrey的回答非常好。

答案 7 :(得分:14)

我经常使用FluentIterable.from(myIterable).toList()

答案 8 :(得分:11)

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

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

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

  3. 如果您碰巧使用 Spring Data JPA,这已经在 JpaRepository

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

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

既然您提到感到沮丧,也许还有一点背景可以帮助您决定使用 Iterable 帮助。

  1. 预计实际需要 Collection 的情况实际上相当罕见,因此在许多情况下应该不会产生任何影响。
  2. 使用覆盖机制可以返回不同的类型,而使用更具体的返回类型(如 Collection)是不可能的。 这将导致无法返回 Streamable,该 Streamable 用于商店可能决定在获取所有元素之前返回结果的情况。
  3. List 实际上是一种灵活的返回类型,因为它提供了到 SetStreamIterable 的简单转换,并且本身就是一个 import numpy as np list1 = [1, 2, 3, 4] list2 = [20, 30, 40, 50, 60, 70, 80, 90] array1 = np.array(list1) array2 = np.array(list2) columns = len(list1) rows = len(list2) matrix = np.zeros((rows, columns)) for column in range(0, columns): for row in range(2*column, rows): matrix[row, column] = round(10 * (array2[row] - array1[column]), 0) print(matrix) 。但这需要您在应用程序中使用许多用户不喜欢的 Spring Data 特定类型。

有一个 section about this in the reference documentation

答案 9 :(得分:10)

在尝试获取ListProject而不是在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();
}

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

答案 10 :(得分:8)

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

答案 11 :(得分:6)

我使用自定义实用程序来投射现有的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不允许使用可能会产生不良结果的空值。

<强>试验:

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

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

答案 12 :(得分:5)

在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回答的启发。

答案 13 :(得分:5)

只要您致电containscontainsAllequalshashCoderemoveretainAllsizetoArray,无论如何,你必须遍历这些元素。

如果您偶尔只调用isEmptyclear等方法,我认为您最好通过懒惰创建集合。例如,您可以使用后备ArrayList来存储以前迭代的元素。

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

答案 14 :(得分:4)

由于RxJava是一把锤子,这看起来像钉子,你可以做到

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

答案 15 :(得分:3)

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

答案 16 :(得分:2)

两个评论

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

答案 17 :(得分:1)

Cactoos尝试StickyList

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

答案 18 :(得分:1)

我没有看到没有任何依赖性的简单的一线解决方案。 我简单地使用

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

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());

答案 19 :(得分: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转换为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的提交者。

答案 20 :(得分:0)

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

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