简体   繁体   English

java - 如何以懒惰的方式连接Java中的两个集合?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM