将Iterable转换为Collection的简单方法


424

在我的应用程序中,我使用3rd party库(确切地说是MongoDB的Spring数据)。

该库的方法返回Iterable<T>,而我的其他代码则期望Collection<T>

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


3
无论如何,执行该操作的任何实用方法都必须迭代该集合,因此您不能指望任何性能提升。但是,如果您只是在寻找语法糖,那我会去买Guava或Apache Collections。
塞巴斯蒂安·甘斯兰特2011年

无论如何一定要迭代集合 ”,-不,不是。请参阅我的答案以获取详细信息。
aioobe 2011年

3
在您的特定用例中,您可以使用自己的接口扩展CrudRepository,并使用返回Collection <T> / List <T> / Set <T>(根据需要)而不是Iterable <T>的方法来代替
Kevin Van Dyck

Answers:


387

使用Guava可以使用Lists.newArrayList(Iterable)Sets.newHashSet(Iterable)以及其他类似方法。当然,这会将所有元素复制到内存中。如果这是不可接受的,我认为您应该使用适合这些的代码,Iterable而不是Collection。番石榴还碰巧提供了方便的方法来完成您可以Collection使用进行的操作Iterable(例如Iterables.isEmpty(Iterable)Iterables.contains(Iterable, Object)),但是对性能的影响更为明显。


1
是否直接遍历所有元素?即是Lists.newArrayList(Iterable).clear()线性还是恒定时间操作?
aioobe 2011年

2
@aioobe:它创建可迭代的副本。并未指定是否需要视图,并且鉴于Collection不能针对Iterable或不能实现视图的大多数方法来实现,所以这样做对我没有多大意义。
ColinD 2011年

@ColinD如果我要查看该怎么办?实际上,我想要的是一个Collection视图,该视图是在源Collection中附加另一个元素的结果。我可以使用,Iterables.concat()但这会给出一个Iterable,而不是一个Collection:(
Hendy Irawan

1
这是我的问题:stackoverflow.com/questions/4896662/…。不幸的是,不能解决问题的简单答案是使用Iterables.concat()。更长的答案给出了Collection...我想知道为什么不更普遍地支持它?
汉迪·爱侣湾

365

在JDK 8+中,不使用任何其他库:

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

编辑:上面的是Iterator。如果您正在处理Iterable

iterable.forEach(target::add);

86
iterable.forEach(target::add);
Cephalopod 2015年

92

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

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

33
+1如果唯一要Iterable考虑的Collection是从到,我宁愿使用这种方法而不是导入大型的3rd party collections-library。
aioobe 2011年

2
与2 MB的已编译库代码(其中99%的代码未使用)相比,4行功能代码更为可取。还有另一个成本:许可复杂化。Apache 2.0许可证很灵活,但并非没有繁琐的命令。理想情况下,我们会看到其中一些常见模式直接集成到Java运行时库中。
乔纳森·诺伊菲尔德

2
还有一点,既然无论如何都在使用ArrayList,为什么不简单地使用协变量List类型呢?这使您可以满足更多合同,而无需向下转换或重新组合,并且Java始终不支持较低的类型界限。
乔纳森·诺伊菲尔德

@JonathanNeufeld还是为什么不继续前进并返回ArrayList <T>?
胡安

5
@Juan因为不是很SOLID。ArrayList公开了很可能不必要的实现细节(YAGNI),这违反了单一责任和依赖关系反转原则。我将其保留在List上,因为它确实比Collection暴露更多,同时完全保持SOLID。如果您担心操作码INVOKEINTERFACE相对于INVOKEVIRTUAL对JVM性能的影响,那么许多基准测试都表明值得一试。
乔纳森·诺伊菲尔德

80

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

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

1
相较IteratorUtilscommons-collections
Alex Burdusel,2013年

3
慢多少?IteratorUtils.toList()以Java 5之前的方式使用迭代器将元素一个接一个地添加到新创建的列表中。简单,可能最快,但是会增加734 kB的二进制文件,如果您发现这种方法是最好的,则可以自己完成。
xehpuk

8
我已经完成了原始基准测试,得出的结论是有时第一个速度更快,有时第二个速度更快。向我们展示您的基准。
xehpuk '16

这个问题可能是新的接受的答案-避免过多的lib(例如Guava)很不错。
java-addict301

48

IteratorUtilscommons-collections5月份的帮助(虽然他们不支持最新的稳定版本3.2.1仿制药):

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

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

更新:IterableAsListCactoos检查。


5
但这需要迭代器,而不是可迭代器
hithwen

5
@hithwen,我不明白– Iterable提供了一个Iterator(如答案中所述)–有什么问题?
汤姆,

不知道我在想什么^^ U
hithwen '16

2
从4.1开始,还提供了IterableUtils.toList(Iterable),这是一种方便的方法,可以IteratorUtils在后台使用,但它也是null安全的(与不同IteratorUtils.toList)。
Yoory N.

21

来自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()线性还是恒定时间操作?
aioobe 2011年

我添加了的源代码addAll,顾名思义,它一个接一个地复制迭代器值。它创建一个副本而不是一个视图。
Tomasz Nurkiewicz 2011年

可惜没有办法CollectionUtils跳过额外的一行来创建集合。
卡尔·里希特

断开链接☝️☝️
HOLA大豆埃杜费利斯纳维达

14

在进行此操作时,请不要忘记所有集合都是有限的,而Iterable没有任何承诺。如果某些东西是可迭代的,那么您可以获取一个迭代器。

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

将扩展为:

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

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

否则:Atrey的答案很好。


1
在实践/实际代码中,有没有人真正遇到过迭代无限(例如答案中给出的自然数示例)的Iterable?我认为这样的Iterable会在许多地方引起痛苦和痛苦……:)
David

2
@David尽管在我的任何生产代码中都无法具体指向无限迭代器,但我可以想到它们可能发生的情况。电子游戏可能具有以上述答案所建议的周期性模式创建商品的技能。虽然我没有遇到任何无限迭代器,但我确实遇到了真正关心内存的迭代器。我在磁盘上的文件上有迭代器。如果我有完整的1TB磁盘和4GB的ram,则将迭代器转换为Collection时很容易用光内存。
7

14

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


9
应该注意的是它也是来自番石榴。
Vadzim

或来自org.apache.commons.collections4。然后,它的FluentIterable.of(myIterable).toList()
DU-它

9

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


2
我会提倡使用通用的CrudRepository以避免将您的代码绑定到具体的实现。
stanlick 2015年

7

如果可用,我将使用自定义实用程序强制转换现有的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等)实现了类似的实用程序。我认为这些已经是番石榴的一部分,但是我还没有找到。


1
您一岁的答案是一个新问题的基础stackoverflow.com/questions/32570534/…吸引了很多观点和评论。
Paul Boddington 2015年

6

只要你打电话containscontainsAllequalshashCoderemoveretainAllsize或者toArray,你无论如何都要穿越元素。

如果您偶尔只调用诸如isEmptyclear我想这样的方法,则最好通过延迟创建集合来更好。例如,您可能有一个支持ArrayList来存储先前迭代的元素。

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



6

在Java 8,你可以做到这一点从一个添加的所有元素IterableCollection并返回它:

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

受到@Afreys答案的启发。


5

由于RxJava是一把锤子,看起来有点像钉子,所以您可以

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

23
有没有办法让jquery参与进来?
德米特里·明科夫斯基

3
如果RxJava中没有null项目,则会崩溃。是不是?
MBH

我相信RxJava2不允许使用null项目,在RxJava中应该没问题。
DariusL

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

2

两句话

  1. 无需将Iterable转换为Collection即可使用foreach循环-Iterable可以直接在这样的循环中使用,在语法上没有区别,因此我几乎不明白为什么最初提出这个问题。
  2. 建议将Iterable转换为Collection的方法是不安全的(与CollectionUtils相同)-无法保证对next()方法的后续调用会返回不同的对象实例。而且,这种关注并非纯粹是理论上的。例如,用于将值传递给Hadoop Reducer的reduce方法的Iterable实现始终返回相同的值实例,只是具有不同的字段值。因此,如果从上方(或CollectionUtils.addAll(Iterator))应用makeCollection,则最终将得到具有所有相同元素的集合。

2

我碰到过类似的情况来试图获取一个ListProjectS,而不是默认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();
}

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



1

我没有没有任何依赖性的简单的单行解决方案。我简单使用

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

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.