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

2022-09-02 12:15:37

有没有一种合理的方法可以从列表(特别是数组列表,但没关系)中获取有序流,该列表以与原始列表中相反的方式流式传输元素?

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

到目前为止,我在这里看到的最干净的方法是实现我自己的版本,并以反向方式推进列表,或者实现反向迭代的a,并在其上使用。SpliteratorORDEREDIteratorSpliterators.spliteratorUnknownSize(iterator,ORDERED)

请注意,这个问题与Java 8流反向顺序不同:另一个问题询问如何反转流(这在一般情况下是不可能的),答案提供了以某种方式反转源(我不想这样做),然后流式传输反转的源。反转源的成本是O(N),如果可能的话,我想完全避免它。


答案 1

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

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

以创建具有特征并提供完全拆分支持的a。StreamORDERED | SIZED | SUBSIZED

但是,对于像这样非随机访问列表,这将是一场性能灾难,但是,谁会使用呢?LinkedListLinkedList

您也可以先通过随机访问进行检查...list instanceof


答案 2

注意:如果您有一个或其他列表允许按索引()进行随机访问检索,那么Holger的方法更可取。仅当数据结构允许反向遍历但不允许索引访问时,才需要以下方法。ArrayListget(i)


不幸的是,似乎没有一个真正简单的(即单行)方法来做到这一点。但是,使用反向流并不太困难,因为它已经具有反向迭代的能力。下面是一个实用程序方法来执行此操作:AbstractSpliteratorList

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);
}

(我想拆分器可能是 ,但这大多是没有意义的,因为这是一个不可拆分的拆分器SIZED

就目前而言,这可以提供有限程度的并行性,就像多次调用和批量工作以移交给分叉连接任务一样。但它不如能够分裂那么有效。AbstractSpliteratortryAdvance

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


推荐