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

Paw*_*lov 18 java java-8 java-stream

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

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

到目前为止,我在这里看到的最干净的方法之一是实现我自己的版本的SpliteratorORDERED和在列表中反向前进,或实施Iterator反向迭代是,并使用Spliterators.spliteratorUnknownSize(iterator,ORDERED)它.

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

Hol*_*ger 19

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

int num=list.size()-1;
IntStream.rangeClosed(0, num).mapToObj(i->list.get(num-i))
Run Code Online (Sandbox Code Playgroud)

创造一个Stream具有特征ORDERED | SIZED | SUBSIZED并提供完全分裂支持的东西.

LinkedList然而,对于非随机访问列表来说,这将是一场性能灾难,无论如何都会使用LinkedList

您也可以先通过list instanceofRandomAccess... 查询

  • 嘿,我应该先想到这个.+1.我想我太喜欢写反转分裂器了. (2认同)
  • 或者`IntStream.rangeClosed(1, list.size()).mapToObj(i -> list.get(list.size() - i))`。 (2认同)

Stu*_*rks 6

注意:如果您有一个ArrayList或其他列表允许通过index(get(i))进行随机访问检索,那么Holger的方法更可取.如果您的数据结构允许反向遍历但不允许索引访问,则只需要以下方法.


不幸的是,似乎没有一种非常简单的方法(即单线程)来做到这一点.但是,使用反向流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);
}
Run Code Online (Sandbox Code Playgroud)

(我想Spliterator可能是SIZED,但这是毫无意义的,因为这是一个不可分割的分裂器.)

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

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

  • @PawelVeselov(我认为你的意思是'AbstractSpliterator`?)有一些权衡.`IteratorSpliterator`不需要`HoldingConsumer`,但它确实需要为每个元素进行额外的方法调用(hasNext + next).但这可能会被"Consumer.accept"调用所抵消......嗯.嗯,我认为找出的唯一真正的方法是衡量.(是的,已经有一段时间了!):-) (2认同)

M. *_*tin 6

从 Java 21 开始,该reversed()方法可用于返回列表上的反向视图,然后可以对其进行流式传输:

Stream<Object> reversedStream = list.reversed().stream();
Run Code Online (Sandbox Code Playgroud)

根据要求,这不会缓冲或复制数据,因为这只是原始列表的反向视图。


tep*_*pic 5

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


小智 5

我最近找到了这个解决方案。

public <T> Stream<T> toReverseStream(List<T> list) {
    ListIterator<T> listIterator = list.listIterator(list.size());
    return Stream.generate(listIterator::previous)
            .limit(list.size());
}
Run Code Online (Sandbox Code Playgroud)