在我的应用程序中,我使用第三方库(Spring Data for MongoDB准确地说)。
这个库的方法返回Iterable<T>,而我的其余代码期望Collection<T>。
有没有什么实用的方法可以让我快速地把一个转换成另一个?为了这么简单的事情,我希望避免在代码中创建一堆foreach循环。
在我的应用程序中,我使用第三方库(Spring Data for MongoDB准确地说)。
这个库的方法返回Iterable<T>,而我的其余代码期望Collection<T>。
有没有什么实用的方法可以让我快速地把一个转换成另一个?为了这么简单的事情,我希望避免在代码中创建一堆foreach循环。
当前回答
下面是一个在Java 8中实现这一功能的SSCCE
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);
}
}
其他回答
您可以使用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();
以上所有可变类型都扩展了java.util.Collection。
注意:我是Eclipse Collections的提交者。
我使用我的自定义实用程序强制转换现有的集合(如果可用)。
主要:
public static <T> Collection<T> toCollection(Iterable<T> iterable) {
if (iterable instanceof Collection) {
return (Collection<T>) iterable;
} else {
return Lists.newArrayList(iterable);
}
}
理想情况下,上面将使用immutabelist,但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等)实现了类似的实用程序。我以为这些已经是番石榴的一部分了,但我还没找到。
尽管如此,不要忘记所有的集合都是有限的,而Iterable没有任何承诺。如果某个东西是Iterable,你可以得到一个Iterator,就是这样。
for (piece : sthIterable){
..........
}
将扩大到:
Iterator it = sthIterable.iterator();
while (it.hasNext()){
piece = it.next();
..........
}
it.hasNext()不需要返回false。因此,在一般情况下,您不能期望能够将每个Iterable转换为一个集合。例如,你可以遍历所有正自然数,遍历带有循环的东西,反复产生相同的结果,等等。
除此之外:Atrey的回答很好。
当你从Spring Data中获得你的Iterable时,你有两个额外的选择。
You can override the method that returns the Iterable in the repository with a version that returns a List, Set or Streamable. This way Spring Data is doing the conversion for you. You may do so in a super interface of your repositories so you don't have to repeat the override in all your repository interfaces. If you happen to use Spring Data JPA this is already done for you in JpaRepository You may do the conversion using the just mentioned Streamable yourself: Iterable<X> iterable = repo.findAll(); List<X> list = Streamable.of(iterable).toList();
既然你提到了沮丧,也许还有一些决定使用Iterable帮助的背景。
It is expected that it is actually fairly rare to actually require a Collection so in many cases it shouldn't make a difference. Using the overriding mechanics one can return different types which wouldn't be possible with a more specific return type like Collection. This would make it impossible to return a Streamable which is intended for cases where a store may decide to return a result before all elements have been fetched. Streamable would actually be a flexible return type, since it offers easy conversions to List, Set, Stream and is itself an Iterable. But this would require you to use a Spring Data specific type in your application which many users wouldn't like.
在参考文档中有关于这方面的章节。
如果您可以更新到Spring Data 3,那么这个问题已经解决了。有一个新的界面ListCrudRepository,它做的正是你想要的。
这是来自https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0:的界面
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
<S extends T> List<S> saveAll(Iterable<S> entities);
List<T> findAll();
List<T> findAllById(Iterable<ID> ids);
}
注意,在版本3中必须实现两个接口
所以在版本2中
public interface PersonRepository<Person, Long> extends
PagingAndSortingRepository<Person, Long> {}
在版本3中应改为:
public interface PersonRepository<Person, Long> extends
PagingAndSortingRepository<Person, Long>,ListCrudRepository<Person, Long> {}
其他变化见https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0