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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29403614/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:11:59  来源:igfitidea点击:

How to get ordered stream from a list in reverse order in Java 8

javajava-8java-stream

提问by Pawel Veselov

Is there a sane way to get an ordered stream from a list (array list specifically, but it shouldn't matter) that streams elements in reverse of how they are in the original list?

是否有一种理智的方法可以从列表(特别是数组列表,但应该无关紧要)中获取有序流,该流以与原始列表中的元素相反的方式流过元素?

I'm looking for a solution that doesn't involve buffering data in anything (collector, another list, array, etc, because they copy the container which is wasteful), or uses Collections.reverse(because it modifies the list).

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

So far, the cleanest ways that I see here is to implement my own version of Spliteratorthat's ORDEREDand advances through the list in reverse, or implement an Iteratorthat iterates in reverse, and use Spliterators.spliteratorUnknownSize(iterator,ORDERED)on it.

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

Note this question is different from Java 8 stream reverse order: that other question asks on how to reverse a stream (which is impossible in general case), and answers offer to reverse the source somehow (which I don't want to do), and then stream that reversed source. The cost of reversing the source is O(N), and I want to avoid it at all if possible.

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

回答by Holger

If your Listis a random access list, you may simply use

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

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

to create a Streamwhich has the characteristics ORDERED | SIZED | SUBSIZEDand offers full splitting support.

创建一个Stream具有特性ORDERED | SIZED | SUBSIZED并提供完整拆分支持的产品。

For a non-random access list like LinkedListit would be a performance disaster, however, who uses LinkedListanyway?

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

You may also check via list instanceofRandomAccessfirst…

您也可以通过list instanceofRandomAccess首先检查…

回答by Stuart Marks

NOTE:If you have an ArrayListor other list that allows random-access retrieval by index (get(i)) then Holger's approachis preferable. The approach below is only necessary if you have a data structure that allows reverse traversal but not indexed access.

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



Unfortunately there doesn't seem to be a really simple (i.e., a one-liner) way to do this. But getting a reversed stream using AbstractSpliteratorisn't too difficult, given that Listalready has the ability to iterate in reverse. Here's a utility method to do that:

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

(I suppose the Spliterator could be SIZED, but that's mostly pointless because this is an unsplittable spliterator.)

(我想 Spliterator 可能是SIZED,但这几乎毫无意义,因为这是一个不可拆分的 spliterator。)

As it stands, this can afford a limited degree of parallelism, as AbstractSpliteratorwill call tryAdvancemultiple times and batch up work to hand off to fork-join tasks. But it's not as efficient as being able to split.

就目前而言,这可以提供有限程度的并行性,因为AbstractSpliterator它将tryAdvance多次调用并批量处理工作以移交给 fork-join 任务。但这并不像能够拆分那样有效。

If parallel efficiency is a great concern, one could write a spliterator that can actually split, where the splits are traversed in reverse order.

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

回答by teppic

Google's Guava library provides a reverse view of a list (Lists#reverse(List)). There's a ReverseListIteratorin the Apache Commons Collection library, too.

Google 的 Guava 库提供了列表 ( Lists#reverse(List))的反向视图。有一个ReverseListIterator在Apache下议院收藏库了。

回答by Matt McHenry

I tend to like @teppic's answer of using a third-party library to do this. However, it's an interesting exercise to try to come up with a solution using only the Java 8 APIs. Delegating to a ListIteratoris the cleanest thing I could come up with, but it's not really much cleaner than implementing your own Iteratorfrom scratch.

我倾向于喜欢@teppic 使用第三方库来做到这一点的答案。但是,尝试提出仅使用 Java 8 API 的解决方案是一项有趣的练习。委托给 aListIterator是我能想到的最干净的东西,但它并不比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();
        }
    };
}