Java 8 流逆序 [英] Java 8 stream reverse order
问题描述
一般问题:反转流的正确方法是什么?假设我们不知道流由什么类型的元素组成,那么反转任何流的通用方法是什么?
General question: What's the proper way to reverse a stream? Assuming that we don't know what type of elements that stream consists of, what's the generic way to reverse any stream?
具体问题:
IntStream
提供 range 方法来生成特定范围内的整数 IntStream.range(-range, 0)
,现在我想将其从 0 转换为负数不起作用,我也不能使用 Integer::compare
IntStream
provides range method to generate Integers in specific range IntStream.range(-range, 0)
, now that I want to reverse it switching range from 0 to negative won't work, also I can't use Integer::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
使用 IntStream
我会得到这个编译器错误
with IntStream
I'll get this compiler error
Error:(191, 0) ajc: IntStream
类型中的方法 sorted()
不适用于参数 (Integer::compare
)
Error:(191, 0) ajc: The method
sorted()
in the typeIntStream
is not applicable for the arguments (Integer::compare
)
我在这里遗漏了什么?
推荐答案
对于生成反向IntStream
的具体问题,试试这样的:
For the specific question of generating a reverse IntStream
, try something like this:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to)
.map(i -> to - i + from - 1);
}
这避免了装箱和排序.
对于如何反转任何类型的流的一般问题,我不知道有一种正确"的方法.我可以想到几种方法.两者最终都存储了流元素.我不知道有什么方法可以在不存储元素的情况下反转流.
For the general question of how to reverse a stream of any type, I don't know of there's a "proper" way. There are a couple ways I can think of. Both end up storing the stream elements. I don't know of a way to reverse a stream without storing the elements.
第一种方法将元素存储到数组中,然后以相反的顺序将它们读出到流中.请注意,由于我们不知道流元素的运行时类型,因此我们无法正确键入数组,需要进行未经检查的强制转换.
This first way stores the elements into an array and reads them out to a stream in reverse order. Note that since we don't know the runtime type of the stream elements, we can't type the array properly, requiring an unchecked cast.
@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
Object[] temp = input.toArray();
return (Stream<T>) IntStream.range(0, temp.length)
.mapToObj(i -> temp[temp.length - i - 1]);
}
另一种技术使用收集器将项目累积到一个反向列表中.这在 ArrayList
对象的前面做了很多插入,所以有很多复制正在进行.
Another technique uses collectors to accumulate the items into a reversed list. This does lots of insertions at the front of ArrayList
objects, so there's lots of copying going on.
Stream<T> input = ... ;
List<T> output =
input.collect(ArrayList::new,
(list, e) -> list.add(0, e),
(list1, list2) -> list1.addAll(0, list2));
可能可以使用某种自定义数据结构编写更高效的反向收集器.
It's probably possible to write a much more efficient reversing collector using some kind of customized data structure.
更新 2016-01-29
由于这个问题最近引起了一些关注,我想我应该更新我的答案以解决在 ArrayList
前面插入的问题.对于大量元素,这将是非常低效的,需要 O(N^2) 次复制.
Since this question has gotten a bit of attention recently, I figure I should update my answer to solve the problem with inserting at the front of ArrayList
. This will be horribly inefficient with a large number of elements, requiring O(N^2) copying.
最好使用 ArrayDeque
代替,它有效地支持在前面插入.一个小问题是我们不能使用 Stream.collect()
的三参数形式;它需要将第二个 arg 的内容合并到第一个 arg 中,并且 Deque
上没有全部添加"批量操作.相反,我们使用 addAll()
将第一个 arg 的内容附加到第二个的末尾,然后返回第二个.这需要使用 Collector.of()
工厂方法.
It's preferable to use an ArrayDeque
instead, which efficiently supports insertion at the front. A small wrinkle is that we can't use the three-arg form of Stream.collect()
; it requires the contents of the second arg be merged into the first arg, and there's no "add-all-at-front" bulk operation on Deque
. Instead, we use addAll()
to append the contents of the first arg to the end of the second, and then we return the second. This requires using the Collector.of()
factory method.
完整代码如下:
Deque<String> output =
input.collect(Collector.of(
ArrayDeque::new,
(deq, t) -> deq.addFirst(t),
(d1, d2) -> { d2.addAll(d1); return d2; }));
结果是一个 Deque
而不是 List
,但这应该不是什么大问题,因为它现在可以很容易地迭代或流式传输-顺序颠倒.
The result is a Deque
instead of a List
, but that shouldn't be much of an issue, as it can easily be iterated or streamed in the now-reversed order.
这篇关于Java 8 流逆序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!