首页 文章

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

提问于
浏览
17

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

我需要在任何事物中缓冲数据(收集器,另一个列表,数组等,因为它们复制了浪费的容器),或使用 Collections.reverse (因为它修改了列表) .

到目前为止,我在这里看到的最简洁的方法是实现我自己的 Spliterator 版本 ORDERED 并反向推进列表,或实现反向迭代的 Iterator ,并使用 Spliterators.spliteratorUnknownSize(iterator,ORDERED) .

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

4 回答

  • 6

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

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

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

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

    您也可以先通过 list instanceof RandomAccess进行检查......

  • 17

    NOTE: 如果您有 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

    我倾向于喜欢@teppic 's answer of using a third-party library to do this. However, it'这是一个有趣的练习,试图仅使用Java 8 API来提出解决方案 . 委托 ListIterator 是我能想到的最干净的东西,但它并不比从头开始实现你自己的_707846更干净 .

    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();
            }
        };
    }
    
  • 2

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

相关问题