如何在Java 8中以相反的顺序从列表中获取有序流

时间:2015-04-02 01:34:20

标签: java java-8 java-stream

是否有一种理智的方法从列表中获取有序流(具体来说,数组列表,但它不应该重要)流式元素与它们在原始列表中的方式相反?

我正在寻找一种不涉及缓冲数据的解决方案(收集器,其他列表,数组等,因为它们复制了浪费的容器),或者使用Collections.reverse (因为它修改了列表)。

到目前为止,我在这里看到的最简洁的方法是实现我自己的Spliterator版本ORDERED并反过来推进列表,或实现{{1}反向迭代,并在其上使用Iterator

请注意,此问题与Java 8 stream reverse order不同:其他问题询问如何撤消流(这在一般情况下是不可能的),并且答案提供了以某种方式扭转来源(我不会这样做)想做),然后传输反向源。逆转源的成本是O(N),如果可能的话我想完全避免它。

4 个答案:

答案 0 :(得分:18)

如果您的List是随机访问列表,则可以使用

int num=list.size()-1;
IntStream.rangeClosed(0, num).mapToObj(i->list.get(num-i))

创建具有Stream特征的ORDERED | SIZED | SUBSIZED,并提供完整的拆分支持。

对于像LinkedList这样的非随机访问列表,这会是一场性能灾难,但是,无论如何,谁使用LinkedList

您也可以先通过list instanceof RandomAccess查看...

答案 1 :(得分:6)

注意:如果您有ArrayList或其他列表允许按索引(get(i))进行随机访问检索,则最好使用Holger's approach。只有当您拥有允许反向遍历但不允许索引访问的数据结构时,才需要使用以下方法。


不幸的是,似乎没有一种非常简单(即单行)的方式来做到这一点。但是,使用AbstractSpliterator获取反向流并不太困难,因为List已经具有反向迭代的能力。这是一种实用方法:

static <T> Stream<T> reversedStream(List<? extends T> input) {
    ListIterator<? extends T> li = input.listIterator(input.size());
    return StreamSupport.stream(
        new Spliterators.AbstractSpliterator<T>(input.size(), Spliterator.ORDERED) {
            @Override public boolean tryAdvance(Consumer<? super T> action) {
                if (li.hasPrevious()) {
                    action.accept(li.previous());
                    return true;
                } else {
                    return false;
                }
            }
        },
        false);
}

(我猜Spliterator可能是SIZED,但这大部分毫无意义,因为这是一个unsplittable spliterator。)

就目前而言,这可以提供有限程度的并行性,因为AbstractSpliterator将多次调用tryAdvance并批量处理以交付到fork-join任务。但它没有能够分裂的效率。

如果并行效率是一个很大的问题,那么可以编写一个实际可以拆分的分裂器,其中拆分以相反的顺序遍历。

答案 2 :(得分:2)

Google的Guava库提供了列表的反向视图(Lists#reverse(List))。 Apache Commons Collection库中也有一个ReverseListIterator

答案 3 :(得分:2)

我倾向于喜欢@ teppic使用第三方库来做到这一点的答案。但是,尝试仅使用Java 8 API提出解决方案是一项有趣的练习。委托给ListIterator是我能想到的最干净的事情,但它并不比从头开始实施你自己的Iterator更清洁。

public static void main(String[] args){
    List<String> l = Arrays.asList("first", "second", "third");

    StreamSupport.stream(Spliterators.spliterator(revit(l), l.size(), 0), false)
                 .forEachOrdered(System.out::println);
}

private static final <T> Iterator<T> revit(List<T> l){
    ListIterator<T> li = l.listIterator(l.size());

    return new Iterator<T>(){
        @Override
        public boolean hasNext(){
            return li.hasPrevious();
        }

        @Override
        public T next(){
            return li.previous();
        }
    };
}