在我的应用程序中,我使用 3rd 方库(确切地说是用于 MongoDB 的 Spring Data)。
这个库的方法返回Iterable<T>
,而我的代码的其余部分期望Collection<T>
。
是否有任何实用方法可以让我快速将一种转换为另一种?我想避免foreach
在我的代码中为这么简单的事情创建一堆循环。
在我的应用程序中,我使用 3rd 方库(确切地说是用于 MongoDB 的 Spring Data)。
这个库的方法返回Iterable<T>
,而我的代码的其余部分期望Collection<T>
。
是否有任何实用方法可以让我快速将一种转换为另一种?我想避免foreach
在我的代码中为这么简单的事情创建一堆循环。
使用Guava,您可以使用Lists.newArrayList(Iterable)或Sets.newHashSet(Iterable)以及其他类似方法。这当然会将所有元素复制到内存中。如果这是不可接受的,我认为你的代码应该使用这些Iterable
而不是Collection
. Guava 也恰好提供了方便的方法来做你可以在Collection
using an Iterable
(例如Iterables.isEmpty(Iterable)
or Iterables.contains(Iterable, Object)
)上做的事情,但对性能的影响更明显。
在 JDK 8+ 中,不使用任何额外的库:
Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);
编辑:以上是针对Iterator
. 如果你正在处理Iterable
,
iterable.forEach(target::add);
您也可以为此编写自己的实用程序方法:
public static <E> Collection<E> makeCollection(Iterable<E> iter) {
Collection<E> list = new ArrayList<E>();
for (E item : iter) {
list.add(item);
}
return list;
}
使用 Java 8 的简洁解决方案java.util.stream
:
public static <T> List<T> toList(final Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false)
.collect(Collectors.toList());
}
IteratorUtils
fromcommons-collections
可能会有所帮助(尽管它们在最新的稳定版本 3.2.1 中不支持泛型):
@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());
4.0 版(目前在 SNAPSHOT 中)支持泛型,您可以摆脱@SuppressWarnings
.
更新:IterableAsList
从Cactoos检查。
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());
}
}
当您Iterable
从 Spring Data 获取您的数据时,您有几个额外的选择。
您可以使用返回或的版本覆盖Iterable
存储库中返回List
的方法。这样,Spring Data 就会为您进行转换。Set
Streamable
您可以在存储库的超级界面中执行此操作,因此您不必在所有存储库接口中重复覆盖。
如果您碰巧使用 Spring Data JPA,这已经为您完成了JpaRepository
您可以使用刚才提到的Streamable
自己进行转换:
Iterable<X> iterable = repo.findAll();
List<X> list = Streamable.of(iterable).toList();
既然你提到了心烦意乱,也许还有一点背景来决定使用Iterable
帮助。
Collection
因此在许多情况下它不应该有所作为。Collection
. 这将使得不可能返回Streamable
用于商店可能决定在获取所有元素之前返回结果的情况。Streamable
实际上是一个灵活的返回类型,因为它提供了到,的简单转换List
,并且它本身就是一个. 但这将要求您在应用程序中使用许多用户不喜欢的 Spring Data 特定类型。Set
Stream
Iterable
参考文档中有一个关于此的部分。
在此过程中,不要忘记所有集合都是有限的,而 Iterable 没有任何承诺。如果某些东西是可迭代的,你可以获得一个迭代器,就是这样。
for (piece : sthIterable){
..........
}
将扩展为:
Iterator it = sthIterable.iterator();
while (it.hasNext()){
piece = it.next();
..........
}
it.hasNext() 不需要返回 false。因此,在一般情况下,您不能期望能够将每个 Iterable 转换为 Collection。例如,您可以迭代所有正自然数,迭代其中包含循环的东西,一遍又一遍地产生相同的结果,等等。
否则:Atrey 的回答很好。
我用FluentIterable.from(myIterable).toList()
的很多。
我在尝试获取 a List
of s 时遇到了类似的情况,而不是在接口中声明Project
的默认值。因此,在我的接口(从 扩展)中,我只是声明了返回 a而不是.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();
}
我认为这是最简单的解决方案,不需要转换逻辑或使用外部库。
这不是您问题的答案,但我相信这是您问题的解决方案。该接口org.springframework.data.repository.CrudRepository
确实具有返回的方法,java.lang.Iterable
但您不应使用此接口。而是使用子接口,在你的情况下org.springframework.data.mongodb.repository.MongoRepository
。此接口具有返回类型对象的方法java.util.List
。
如果可用,我使用我的自定义实用程序来转换现有的集合。
主要的:
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));
}
我为集合的所有子类型(集合、列表等)实现了类似的实用程序。我认为这些已经是番石榴的一部分,但我还没有找到。
一旦你调用contains
, containsAll
, equals
, hashCode
, remove
, retainAll
, size
or toArray
,无论如何你都必须遍历元素。
如果您偶尔只调用诸如isEmpty
或之类的方法,clear
我想您最好懒惰地创建集合。例如,您可以为ArrayList
存储先前迭代的元素提供支持。
我不知道任何库中有任何这样的类,但编写起来应该是一个相当简单的练习。
在 Java 8 中,您可以这样做来添加来自Iterable
to的所有元素Collection
并返回它:
public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
Collection<T> collection = new ArrayList<>();
iterable.forEach(collection::add);
return collection;
}
受@Afreys 回答的启发。
由于 RxJava 是一把锤子,这有点像钉子,你可以这样做
Observable.from(iterable).toList().toBlocking().single();
这是一个 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);
}
}
我没有看到没有任何依赖关系的简单单行解决方案。我简单使用
List<Users> list;
Iterable<IterableUsers> users = getUsers();
// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());
两个备注
StickyList
从Cactoos尝试:
List<String> list = new StickyList<>(iterable);
参加聚会有点晚了,但我创建了一个非常优雅的 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);
您可以使用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 的提交者。
尝试窄铸:(List<iterable_type>)iterable;