【问题标题】:How to get ordered stream from a list in reverse order in Java 8如何在Java 8中以相反的顺序从列表中获取有序流
【发布时间】:2015-06-06 20:33:59
【问题描述】:

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

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

到目前为止,我在这里看到的最简洁的方法是实现我自己的Spliterator 版本,即ORDERED,并在列表中反向前进,或者实现反向迭代的Iterator,并使用@987654327 @就可以了。

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

【问题讨论】:

标签: java java-8 java-stream


【解决方案1】:

如果你的List是一个随机访问列表,你可以简单地使用

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

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

对于像LinkedList 这样的非随机访问列表,这将是性能灾难,但是,谁使用LinkedList

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

【讨论】:

  • 呵呵,我应该先想到这个的。 +1。我想我太着迷于编写一个反向拆分器的想法了。
  • IntStream.rangeClosed(1, list.size()).mapToObj(i -> list.get(list.size() - i))
【解决方案2】:

注意:如果您有一个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 任务。但它的效率不如能够拆分。

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

【讨论】:

  • 那么,使用AbstractSpliteratorIteratorSpliterator 更好?
  • 我不认为SIZED 毫无意义。不过SUBSIZED 毫无意义。
  • @PawelVeselov 我认为AbstractSpliterator 比编写自己的迭代器要容易一些。但是如果你已经有一个迭代器,那么使用Spliterators.spliteratorUnknownSize(iterator, ...)——它在下面使用IteratorSpliterator——肯定更容易编码。
  • @Holger 是的,SIZED 可能允许流进行更好的缓冲,但我不确定。好吧,在 JDK 9 中,它允许count() 短路。
  • @PawelVeselov(我想你的意思是AbstractSpliterator?)有一些权衡。 IteratorSpliterator 不需要HoldingConsumer,但它确实需要对每个元素进行额外的方法调用(hasNext+next)。但这可能会被Consumer.accept 调用所抵消......嗯。好吧,我认为找出答案的唯一真正方法是测量。 (是的,已经有一段时间了!):-)
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    我倾向于喜欢@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();
            }
        };
    }
    

    【讨论】:

      猜你喜欢
      • 2011-01-07
      • 2021-10-13
      • 2018-10-05
      • 2015-05-20
      • 2013-05-18
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      相关资源
      最近更新 更多