Java 8流反向顺序
一般问题:反向流的正确方法是什么?假设我们不知道流包含什么类型的元素,反转任何流的通用方法是什么?
具体问题:
IntStream提供了range方法来生成特定范围内的Integer IntStream.range(-range, 0)
,现在我想将其从0切换为负数将无法正常工作,也无法使用Integer::compare
List<Integer> list = Arrays.asList(1,2,3,4);list.stream().sorted(Integer::compare).forEach(System.out::println);
与IntStream我会得到这个编译器错误
错误:(191,0)ajc:sorted()类型中的方法IntStream不适用于参数(Integer::compare)
我在这里想念什么?
回答:
对于产生反向的特定问题IntStream,请尝试如下操作:
static IntStream revRange(int from, int to) { return IntStream.range(from, to)
.map(i -> to - i + from - 1);
}
这样可以避免装箱和分类。
对于如何反转任何类型的流的一般问题,我不知道有“适当的”方法。我可以想到几种方法。两者最终都存储了流元素。我不知道在不存储元素的情况下反转流的方法。
第一种方法将元素存储到数组中,然后以相反的顺序将它们读出到流中。请注意,由于我们不知道stream元素的运行时类型,因此我们无法正确键入数组,这需要未经检查的强制转换。
@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对象的前面进行很多插入操作,因此正在进行大量复制。
Stream<T> input = ... ;List<T> output =
input.collect(ArrayList::new,
(list, e) -> list.add(0, e),
(list1, list2) -> list1.addAll(0, list2));
使用某种自定义数据结构可能会编写出效率更高的可逆收集器。
更新2016-01-29
由于这个问题最近引起了人们的关注,我认为我应该更新自己的答案以解决在前面插入的问题ArrayList
。对于大量元素,这将是极其低效的,需要O(N ^ 2)复制。
最好使用ArrayDeque
有效支持正面插入的。一个小皱纹是我们不能使用的三参数形式Stream.collect()
;它要求将第二个arg的内容合并到第一个arg中,并且在上没有“前添加所有”批量操作Deque
。相反,我们使用addAll()
将第一个arg
的内容附加到第二个arg的末尾,然后返回第二个。这需要使用Collector.of()
工厂方法。
完整的代码是这样的:
Deque<String> output = input.collect(Collector.of(
ArrayDeque::new,
(deq, t) -> deq.addFirst(t),
(d1, d2) -> { d2.addAll(d1); return d2; }));
结果是aDeque
而不是aList
,但这不成问题,因为它可以轻松地以现在相反的顺序进行迭代或流式传输。
以上是 Java 8流反向顺序 的全部内容, 来源链接: utcz.com/qa/398436.html