簡體   English   中英

java - 如何以懶惰的方式連接Java中的兩個集合?

[英]How to concatenate two collections in java in a lazy way?

我有兩個集合,我想返回一個 IEnumerable,它是它們的串聯。 返回的枚舉應該是惰性的,不應修改兩個初始集合(因此,我不想將兩個集合復制為一個然后返回結果,因為那不是惰性的)

下面的代碼是我想用 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);
}

您可以使用Apache Commons Collections方法IterableUtils.chainedIterable(list1, list2)

將提供的可迭代對象組合成一個可迭代對象。

返回的 iterable 有一個迭代器,它按照參數的順序遍歷元素,即 iterables[0], iterables 2 , ....源迭代器只有在必要時才會輪詢。

番石榴方法Iterables.concat(list1, list2)

將多個可迭代對象組合成一個可迭代對象。 返回的可迭代對象有一個迭代器,它遍歷輸入中每個可迭代對象的元素。 除非必要,否則不會輪詢輸入迭代器。

這是一個返回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();
        }
    }
}

你可以得到一個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);
        }
    }
}

可以在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) 查一下。

如果您只需要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);
    }
}

您也可以為List<T>甚至Collection<T>做類似的工作。

如果您的輸入是列表,則ChainedList下面的類可以包裝它們。

我不得不將兩個列表作為單個惰性列表提供給 JAXB Marshaller,但它不適用於Iterable 要創建具有maxOccurs="unbounded"元素,JAXB 需要 bean 屬性至少實現java.util.Collection

該解決方案使用 Apache Commons,但在 Guava 中有一個等效的Iterables.concat()

最新版本和單元測試

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