在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))
。