[英]How to concatenate two collections in java in a lazy way?
I have two collections and I want to return an IEnumerable which is the concatenation of them.我有两个集合,我想返回一个 IEnumerable,它是它们的串联。 The returned enumerable should be lazy and should not modify the two initial collections (So, I do not want to copy the two collection into one and then return the result because that is not lazy)返回的枚举应该是惰性的,不应修改两个初始集合(因此,我不想将两个集合复制为一个然后返回结果,因为那不是惰性的)
The code below is an example of what I want to achieve in Java but written in c#:下面的代码是我想用 Java 实现但用 c# 编写的示例:
public static IEnumerable<int> all()
{
List<int> list1 = new List<int>() { 1, 2, 3 };
List<int> list2 = new List<int>() { 4, 5, 6 };
return list1.Concat(list2);
}
You can use Apache Commons Collections method IterableUtils.chainedIterable(list1, list2)
:您可以使用Apache Commons Collections方法IterableUtils.chainedIterable(list1, list2)
:
Combines the provided iterables into a single iterable.将提供的可迭代对象组合成一个可迭代对象。
The returned iterable has an iterator that traverses the elements in the order of the arguments, ie iterables[0], iterables 2 , .... The source iterators are not polled until necessary.返回的 iterable 有一个迭代器,它按照参数的顺序遍历元素,即 iterables[0], iterables 2 , ....源迭代器只有在必要时才会轮询。
Or Guava method Iterables.concat(list1, list2)
:或番石榴方法Iterables.concat(list1, list2)
:
Combines multiple iterables into a single iterable.将多个可迭代对象组合成一个可迭代对象。 The returned iterable has an iterator that traverses the elements of each iterable in inputs.返回的可迭代对象有一个迭代器,它遍历输入中每个可迭代对象的元素。 The input iterators are not polled until necessary.除非必要,否则不会轮询输入迭代器。
Here's a manual implementation returning an Iterator
.这是一个返回Iterator
的手动实现。
public static <T> Iterator<T> concatIterator(Iterable<T> l1, Iterable<T> l2) {
return new Iterator<>() {
Iterator<T> it1 = l1.iterator();
Iterator<T> it2 = l2.iterator();
public boolean hasNext() {
return it1.hasNext() || it2.hasNext();
}
public T next() {
return it1.hasNext() ? it1.next() : it2.next();
}
}
}
And you can get an Iterable<T>
using that你可以得到一个Iterable<T>
使用它
public static <T> Iterable<T> concatIterable(Iterable<T> l1, Iterable<T> l2) {
return new Iterable<>() {
public Iterator<T> iterator() {
return concatIterator(l1, l2);
}
}
}
A Java equivalent of the C# IEnumerable<TSource> Enumerable.Concat<TSource>(IEnumerable<TSource>, IEnumerable<TSource>)
can be found in Commons Collections: <E> Iterable<E> IterableUtils.chainedIterable(Iterable<? extends E> a, Iterable<? extends E> b)
.可以在Commons Collections: <E> Iterable<E> IterableUtils.chainedIterable(Iterable<? extends E> a, Iterable<? extends E> b)
找到C# IEnumerable<TSource> Enumerable.Concat<TSource>(IEnumerable<TSource>, IEnumerable<TSource>)
的 Java 等效项Commons Collections: <E> Iterable<E> IterableUtils.chainedIterable(Iterable<? extends E> a, Iterable<? extends E> b)
。 Look it up.查一下。
If all you need is Iterable
then a simple Chain
should work for you.如果您只需要Iterable
那么一个简单的Chain
应该适合您。
class Chain<T> implements Iterable<T> {
final Iterable<Iterable<T>> lists;
public Chain(Iterable<T>... lists) {
this.lists = Arrays.asList(lists);
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
// Walks the lists.
Iterator<Iterable<T>> i = lists.iterator();
// Walks the list.
Iterator<T> l = prime(i.hasNext() ? i.next().iterator() : null);
@Override
public boolean hasNext() {
return l != null && l.hasNext();
}
@Override
public T next() {
if (hasNext()) {
T next = l.next();
l = prime(l);
return next;
} else {
throw new NoSuchElementException("Chain exhausted.");
}
}
private Iterator<T> prime(Iterator<T> l) {
// Prepare for next time.
while (l != null && !l.hasNext()) {
if (i.hasNext()) {
l = i.next().iterator();
} else {
l = null;
}
}
return l;
}
};
}
}
public void test(String[] args) {
Chain<Integer> chain = new Chain<>(
Arrays.asList(),
Arrays.asList(1, 2, 3),
Arrays.asList(),
Arrays.asList(4, 5, 6),
Arrays.asList(7,8,9,10),
Arrays.asList()
);
for (Integer i : chain) {
System.out.println(i);
}
}
You can also do similar work for List<T>
and probably even Collection<T>
.您也可以为List<T>
甚至Collection<T>
做类似的工作。
If your inputs are lists then the class below ChainedList
can wrap them.如果您的输入是列表,则ChainedList
下面的类可以包装它们。
I had to feed two lists as a single lazy list to JAXB Marshaller, but it doesn't work with Iterable
.我不得不将两个列表作为单个惰性列表提供给 JAXB Marshaller,但它不适用于Iterable
。 To create elements with maxOccurs="unbounded"
JAXB requires the bean property to implement at least java.util.Collection
.要创建具有maxOccurs="unbounded"
元素,JAXB 需要 bean 属性至少实现java.util.Collection
。
The solution uses Apache Commons, but there's an equivalent Iterables.concat()
in Guava.该解决方案使用 Apache Commons,但在 Guava 中有一个等效的Iterables.concat()
。
Up-to-date version & unit test 最新版本和单元测试
import org.apache.commons.collections4.IterableUtils;
/** This class makes multiple lists look like one to the caller. */
public class /* NOSONAR */ ChainedList<E> extends AbstractList<E> implements RandomAccess {
private final List<E>[] elements;
private final int[] endIndexes;
@SafeVarargs
public ChainedList(final List<E>... lists) {
this(Arrays.asList(lists));
}
@SuppressWarnings("unchecked")
public ChainedList(final Iterable<List<E>> elements) {
final List<List<E>> tmpElementsList = IterableUtils.toList(elements);
this.elements = tmpElementsList.toArray(new List[tmpElementsList.size()]);
endIndexes = new int[this.elements.length];
int currentSize = 0;
for (int i = 0; i < this.elements.length; i++) {
final List<E> curr = this.elements[i];
final int sz = curr.size();
endIndexes[i] = currentSize + sz;
currentSize += sz;
}
}
@Override
public E get(final int index) {
final int partitionIndex = getPartitionIndex(index);
final int subIndex = getSubIndex(partitionIndex, index);
// throws when index >= size()
final List<E> subList = elements[partitionIndex];
// throws when index is negative or last sublist is empty
return subList.get(subIndex);
}
@Override
public E set(final int index, final E element) {
final int partitionIndex = getPartitionIndex(index);
final int subIndex = getSubIndex(partitionIndex, index);
// throws when index >= size()
final List<E> subList = elements[partitionIndex];
if (subIndex < 0 || subIndex >= subList.size()) {
// a sublist may throw unsupported even when index OOB
throw new IndexOutOfBoundsException();
}
return subList.set(subIndex, element);
}
@Override
public Iterator<E> iterator() {
// this may perform better with contained LinkedList
return IterableUtils.chainedIterable(elements).iterator();
}
@Override
public ListIterator<E> listIterator(final int index) {
// indexOf, lastIndexOf, equals, removeRange, subList
// call this method and for non-RandomAccess lists
// the default implementation is slow
// T O D O: implement LazyListIteratorChain similar to
// org.apache.commons.collections4.iterators.LazyIteratorChain
for (final List<E> subList : elements) {
if (!(subList instanceof RandomAccess)) {
throw new UnsupportedOperationException(
"Not RandomAccess: " + subList.getClass().getName());
}
}
return super.listIterator(index);
}
/**
* @return negative value when {@code index} is negative
*/
private int getSubIndex(final int partitionIndex, final int index) {
return index - (partitionIndex == 0 ? 0 : endIndexes[partitionIndex - 1]);
}
private int getPartitionIndex(final int index) {
int location = Arrays.binarySearch(endIndexes, index);
if (location < 0) {
location = (~location) - 1;
}
return location + 1;
}
@Override
public int size() {
return endIndexes.length == 0 ? 0 : endIndexes[endIndexes.length - 1];
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.