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

在Java8中如何从列表中以相反顺序获取有序流,java,java-8,java-stream,Java,Java 8,Java Stream,是否有一种合理的方法可以从一个列表(具体来说是数组列表,但这并不重要)中获得一个有序的流,该列表按照元素在原始列表中的相反方式来流元素 我正在寻找一种解决方案,该解决方案不涉及缓冲任何数据(收集器、另一个列表、数组等,因为它们复制了浪费的容器),或者使用Collections.reverse(因为它修改了列表) 到目前为止,我在这里看到的最干净的方法是实现我自己版本的Spliterator,它是ORDERED并反向遍历列表,或者实现一个反向迭代的Iterator,并在其上使用Spliterato

是否有一种合理的方法可以从一个列表(具体来说是数组列表,但这并不重要)中获得一个有序的流,该列表按照元素在原始列表中的相反方式来流元素

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

到目前为止,我在这里看到的最干净的方法是实现我自己版本的
Spliterator
,它是
ORDERED
并反向遍历列表,或者实现一个反向迭代的
Iterator
,并在其上使用
Spliterator.Spliterator unknownsize(Iterator,ORDERED)


请注意,这个问题不同于:另一个问题询问如何反转一个流(在一般情况下这是不可能的),答案提供了某种方式反转源(我不想这样做),然后流那个反转的源。反转源代码的代价是O(N),如果可能的话,我想避免它。

谷歌的Guava库提供了列表的反向视图()。Apache Commons集合库中也有一个

我倾向于喜欢@teppic关于使用第三方库来实现这一点的回答。然而,尝试提出一个只使用Java8API的解决方案是一个有趣的练习。委托给
列表迭代器
是我能想到的最干净的事情,但它并不比从头开始实现自己的
迭代器
干净多少

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();
        }
    };
}
publicstaticvoidmain(字符串[]args){
listl=Arrays.asList(“第一”、“第二”、“第三”);
StreamSupport.stream(Spliterators.spliterator(revit(l),l.size(),0),false)
.forEachOrdered(System.out::println);
}
专用静态最终迭代器revit(列表l){
ListIterator li=l.ListIterator(l.size());
返回新的迭代器(){
@凌驾
公共布尔hasNext(){
返回li.hasPrevious();
}
@凌驾
公共交通工具{
返回li.previous();
}
};
}

注意:如果您有一个
数组列表或其他允许通过索引进行随机访问检索的列表(
get(i)
),则最好是这样。只有当您的数据结构允许反向遍历但不允许索引访问时,才需要下面的方法


不幸的是,似乎没有一个真正简单的(即一行)方法来实现这一点。但是使用
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);
}

static Stream reversedStream(List如果您的
List
是一个随机访问列表,您只需使用

int num=list.size()-1;
IntStream.rangeClosed(0, num).mapToObj(i->list.get(num-i))
创建一个
,该流具有
有序的|大小的|子流
特征,并提供完整的拆分支持

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


你也可以先通过
列出
的实例来检查…

本文介绍了几种可能性:因此,使用
抽象拆分器
在某种程度上比
迭代器拆分器
好?我不认为
大小
是没有意义的。
子化
将是没有意义的。@PawelVeselov我认为
文章摘要actSpliterator
比编写自己的迭代器要简单一点。但是如果您已经有迭代器,则使用
Spliterator.spliteratorUnknownSize(迭代器,…)
——它在下面使用了
迭代器或迭代器--
当然更容易编码。@Holger是的,
大小
可能允许流做更好的缓冲,但我不确定。在JDK 9中,它允许
计数()
短路。@PawelVeselov(我想你是说
抽象拆分器
?)有一些折衷。迭代器迭代器不需要一个
HoldingConsumer
,但它确实需要为每个元素调用一个额外的方法(hasNext+next)。但这可能会被
Consumer.accept
调用抵消…嗯。我认为唯一真正的方法是测量。(是的,已经有一段时间了!):-)嘿,我应该先想到这个+1.我想我太喜欢编写一个反向拆分器了。或者
IntStream.rangeClosed(1,list.size()).mapToObj(I->list.get(list.size()-I))