如何在 Java 8 中以相反的顺序从列表中获取有序流
How to get ordered stream from a list in reverse order in Java 8
是否有一种明智的方法可以从列表(特别是数组列表,但它应该无关紧要)中获取与原始列表中的元素相反的顺序流?
我正在寻找一种不涉及任何缓冲数据的解决方案(收集器、另一个列表、数组等,因为它们复制容器是浪费的),或者使用 Collections.reverse
(因为它会修改列表)。
到目前为止,我在这里看到的最干净的方法是实现我自己的 Spliterator
版本,即 ORDERED
并反向推进列表,或者实现一个 Iterator
反向迭代,并在其上使用 Spliterators.spliteratorUnknownSize(iterator,ORDERED)
。
注意这个问题与 Java 8 stream reverse order 不同:另一个问题询问如何反转流(这在一般情况下是不可能的),并且答案提供以某种方式反转源(我不这样做)想做),然后流式传输该反向源。逆向源的代价是O(N),如果可能我想完全避免。
Google 的 Guava 库提供了列表的反向视图(Lists#reverse(List)
). There's a ReverseListIterator
在 Apache Commons Collection 库中也是如此。
我倾向于喜欢@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();
}
};
}
注意: 如果您有一个 ArrayList
或其他允许按索引 (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);
}
(我想 Spliterator 可能是 SIZED
,但这几乎没有意义,因为这是一个 unsplittable spliterator。)
就目前而言,这可以提供有限程度的并行性,因为 AbstractSpliterator
将多次调用 tryAdvance
并将工作分批处理以移交给 fork-join 任务。但是效率不如能拆分。
如果并行效率是一个很大的问题,那么可以编写一个可以实际拆分的拆分器,其中拆分以相反的顺序遍历。
如果您的 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
查看...
是否有一种明智的方法可以从列表(特别是数组列表,但它应该无关紧要)中获取与原始列表中的元素相反的顺序流?
我正在寻找一种不涉及任何缓冲数据的解决方案(收集器、另一个列表、数组等,因为它们复制容器是浪费的),或者使用 Collections.reverse
(因为它会修改列表)。
到目前为止,我在这里看到的最干净的方法是实现我自己的 Spliterator
版本,即 ORDERED
并反向推进列表,或者实现一个 Iterator
反向迭代,并在其上使用 Spliterators.spliteratorUnknownSize(iterator,ORDERED)
。
注意这个问题与 Java 8 stream reverse order 不同:另一个问题询问如何反转流(这在一般情况下是不可能的),并且答案提供以某种方式反转源(我不这样做)想做),然后流式传输该反向源。逆向源的代价是O(N),如果可能我想完全避免。
Google 的 Guava 库提供了列表的反向视图(Lists#reverse(List)
). There's a ReverseListIterator
在 Apache Commons Collection 库中也是如此。
我倾向于喜欢@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();
}
};
}
注意: 如果您有一个 ArrayList
或其他允许按索引 (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);
}
(我想 Spliterator 可能是 SIZED
,但这几乎没有意义,因为这是一个 unsplittable spliterator。)
就目前而言,这可以提供有限程度的并行性,因为 AbstractSpliterator
将多次调用 tryAdvance
并将工作分批处理以移交给 fork-join 任务。但是效率不如能拆分。
如果并行效率是一个很大的问题,那么可以编写一个可以实际拆分的拆分器,其中拆分以相反的顺序遍历。
如果您的 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
查看...