将Iterable转换为Collection的简便方法

Ula*_*kar 398 java collections

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

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

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

Col*_*inD 373

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

  • @aioobe:它创建了iterable的副本.没有指定需要一个视图,并且鉴于`Collection`上的大多数方法要么无法实现"Iterable"的视图,要么效率不高,因此没有多大意义我这样做. (2认同)

Tha*_*wda 341

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

Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);
Run Code Online (Sandbox Code Playgroud)

编辑:以上是为了Iterator.如果你正在处理Iterable,

iterable.forEach(target::add);
Run Code Online (Sandbox Code Playgroud)

  • 或者`iterable.forEach(target :: add);` (81认同)

Atr*_*eys 89

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

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

  • +1如果从"Iterable"转到"Collection"是唯一的问题,我更喜欢这种方法而不是导入大型第三方集合库. (30认同)
  • @Juan因为那不是很[实体](https://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29).ArrayList公开了最不可能的实现细节(YAGNI),这违反了单一责任和依赖性反转原则.我会把它留在List,因为它确实暴露了比Collection更多的东西,同时保持完全SOLID.如果你担心操作码INVOKEINTERFACE对INVOKEVIRTUAL的JVM性能影响,那么很多基准测试都会显示它不值得失眠. (5认同)
  • 与2 MB的已编译库代码(其中99%的代码未使用)相比,4行功能代码更为可取。还有另一个成本:许可复杂化。Apache 2.0许可证很灵活,但并非没有繁琐的命令。理想情况下,我们会看到其中一些常见模式直接集成到Java运行时库中。 (2认同)
  • 还有一点,既然你无论如何都在使用ArrayList,为什么不简单地使用协变List类型呢?这允许您在不进行转码或重构的情况下满足更多合同,并且Java无论如何都不支持较低类型的边界. (2认同)

xeh*_*puk 75

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

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

  • 我做了一个原始的基准测试结论,有时第一个更快,有时第二个更快.向我们展示您的基准. (8认同)
  • 慢多少?`IteratorUtils.toList()`以前Java 5方式使用迭代器将元素逐个添加到新创建的列表中.简单且可能最快,但是你的二进制文件增加了734 kB,如果你发现这种方法是最好的,你可以自己做. (3认同)
  • 这个问题可能是新接受的答案 - 避免过多的库(如番石榴)是很好的。 (2认同)

yeg*_*256 48

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

@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());
Run Code Online (Sandbox Code Playgroud)

版本4.0(此时在SNAPSHOT中)支持泛型,你可以摆脱它@SuppressWarnings.

更新:IterableAsListCactoos检查.

  • 但这需要一个迭代器,而不是一个Iterable (5认同)
  • @hithwen,我不明白 - Iterable提供了一个迭代器(详见答案) - 问题是什么? (4认同)
  • 从4.1开始,还有`IterableUtils.toList(Iterable)`,这是一种方便的方法,在幕后使用`IteratorUtils`,但也是空值安全的(不同于`IteratorUtils.toList`)。 (2认同)

Tom*_*icz 20

来自CollectionUtils:

List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())
Run Code Online (Sandbox Code Playgroud)

以下是此实用程序方法的完整来源:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 14

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

for (piece : sthIterable){
..........
}
Run Code Online (Sandbox Code Playgroud)

将扩大到:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}
Run Code Online (Sandbox Code Playgroud)

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

否则:Atrey的答案很好.

  • @David虽然我不能在我的任何生产代码中特别指向无限的迭代器,但我可以想到它们可能发生的情况.视频游戏可能具有以上述答案所暗示的循环模式创建项目的技能.虽然我没有遇到任何无限的迭代器,但我肯定遇到了内存是一个真正关注的迭代器.我在磁盘上的文件上有迭代器.如果我有一个完整的1TB磁盘和4GB内存,我很容易耗尽内存将我的迭代器转换为一个集合. (2认同)

fri*_*ngd 14

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

  • 应该注意的是它也来自番石榴. (8认同)

Man*_*iri 12

我碰到过类似的情况来试图获取一个ListProjectS,而不是默认Iterable<T> findAll()的声明CrudRepository接口。因此,在我的ProjectRepository接口(从 扩展CrudRepository)中,我只是声明了findAll()返回 aList<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();
}
Run Code Online (Sandbox Code Playgroud)

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


Jen*_*der 12

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

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

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

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

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

    Iterable<X> iterable = repo.findAll();
    List<X> list = Streamable.of(iterable).toList();
    
    Run Code Online (Sandbox Code Playgroud)

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

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

参考文档中有一节介绍了这一点


Lud*_*son 9

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

  • 我会提倡使用通用的CrudRepository以避免将您的代码绑定到具体的实现。 (2认同)

Aar*_*ler 7

我使用自定义实用程序来转换现有的Collection(如果可用).

主要:

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

理想情况下,上面将使用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));
}
Run Code Online (Sandbox Code Playgroud)

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


aio*_*obe 6

只要你打电话contains,containsAll,equals,hashCode,remove,retainAll,size或者toArray,你无论如何都要穿越元素.

如果你偶尔只调用这样的方法,isEmpty或者clear我认为通过懒惰创建集合你会更好.例如,您可以支持ArrayList存储以前迭代的元素.

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


小智 5

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

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

受到@Afreys答案的启发。


Dar*_*usL 5

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

Observable.from(iterable).toList().toBlocking().single();
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法让jquery参与进来? (22认同)
  • 如果RxJava中没有null项目,则会崩溃。是不是? (3认同)