假设我有一个像这样的累积数组:
[0,1,3,6,10,15,21,28,36]
我想把它变成
[0,1,2,3,4,5,6,7,8]
,其中每个元素计算为:
value = [n] - [n-1]
有没有办法使用 Java 流 API 来实现此目的?
下面,您可以看到我的代码尝试。
尝试1:
confirmData = confirmedReader.readAll().stream()
.map(cumulativeArr -> Arrays.stream(cumulativeArr).toArray(String[]::new))
.toList();
尝试2:
confirmData = confirmedReader.readAll().stream().skip(1)
.map(a -> {
IntStream.range(0,a.length).forEach(x -> {
a[x] = Integer.toString(Integer.parseInt(a[x]) - Integer.parseInt(a[x - 1]));
});
})
.toList();
使用IntStream能够达到给定的结果:
int[] arr = { 0, 1, 3, 6, 10, 15, 21, 28, 36 };
IntStream.range(0, arr.length - 1)
.map(i -> arr[i + 1] - arr[i])
.forEach(System.out::println);
输出:
1
2
3
4
5
6
7
8
不确定你从哪里获取数据,但假设你能够将其转换为 int 数组,你可以使用
Arrays.setAll
方法:
int[] original = {0,1,3,6,10,15,21,28,36};
int[] result = new int[original.length];
Arrays.setAll(result, i -> i > 0 ? original[i] - original[i-1] : original[i] );
System.out.println(Arrays.toString(result));
“naive”方法将使用 Stream.range()
或
Stream.iterate()
来模仿传统的基于索引的 for
循环。但请注意,Stream本质上是一个内部迭代器,这意味着Streams背后的核心思想是你不控制国际化过程,你不通过它的索引检索下一个元素,而不是检查是否存在。使用流,你需要考虑转换和累加逻辑,但需要考虑迭代的过程。 如果您选择
使用索引拨号,那么@Eritrean介绍的利用Arrays.setAll()
的解决方案是最干净的。
我提出了以下解决方案,该解决方案利用通过
Collector.of()
创建的 自定义收集器
。
Deque
用作收集器的
可变容器的类型(
List
也很好,选择Deque
是因为它有助于方便地访问最后一个元素)。在减少的每一步中,accumulator函数都会轮询最后一个元素(如果存在)并将轮询元素与流中的下一个元素之间的差异添加到deque中,然后它还存储下一个元素本身(请遵循代码中的注释以更好地理解它)。
int[] source = {0, 1, 3, 6, 10, 15, 21, 28, 36};
List<Integer> result = Arrays.stream(source).boxed()
.collect(Collector.of( // Creating a custom Collector
ArrayDeque::new, // Mutable Container of the Collector
(Deque<Integer> deque, Integer next) -> { // Accumulator - adding stream element into the Container
if (deque.isEmpty()) deque.addLast(next);
else deque.addLast(next - deque.pollLast());
deque.addLast(next); // supplementary element which would be used in the else clause of the Accumulator as well as in the Combiner while running parallel and skipped by the Finisher in sequential
},
(left, right) -> { // Combiner - merging Container containing intermediate results while executing in parallel (each thread would be given it's own chunk of data and would create its own Deque which will be populated according to the logic represented in the Accumulator)
left.addLast(right.pollFirst() - left.pollLast()); // supplementary element is being polled from the Left Deque here
left.addAll(right);
return left;
},
deque -> deque.stream().limit(source.length).toList() // Finisher - transforming Deque into a List
));
System.out.println(result);
输出:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
int[] original = { 0, 1, 3, 6, 10, 15, 21, 28, 36 };
List<Integer> ls = IntStream.range(0, original.length).collect(ArrayList<Integer>::new, (x, y) -> {
if (y == 0)
x.add(original[y]);
else
x.add(original[y] - original[y - 1]);
}, List::addAll);
System.out.println(ls);
输出:
[0,1,2,3,4,5,6,7,8]
请注意,这会返回列表输出,正如我根据您的问题所看到的那样,您正在寻找列表输出。
包通过 Ints.indexOf(array , target) : 来精细索引元素
算法:值 = n - (n-1)如果 0 继续,否则 1 = 1 - 0 , 2 = 3 - 1 , 3 = 6 - 3 ... 等等
int[] source = {0, 1, 3, 6, 10, 15, 21, 28, 36}; List<Integer> list = Arrays.stream(source). map(s-> s = s != 0 ? s - source[Ints.indexOf(source, s) - 1] : s). boxed().collect(Collectors.toList()); System.out.println(list);
打印:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
Java 22 预览语言功能添加了对滑动窗口流式传输的内置支持,这非常适合:
list.stream()
.gather(Gatherers.windowSliding(2))
.map(w -> w.getLast() - w.getFirst())
.toList();
这使用新的
方法和新的内置
Gatherers.windowSliding
收集器将初始
Stream<Integer>
转换为成对 Stream<List<Integer>>
。然后使用现有的 Stream.map
方法将这些成对列表映射到元素之间的差异。Java文档
:将输入元素流转换为输出元素流的中间操作,可以选择在到达上游末尾时应用最终操作。 […]
[…]
聚集操作的例子有很多,包括但不限于:将元素分组(窗口函数);对连续相似的元素进行去重;增量累加功能(前缀扫描);增量重新排序功能等。类
提供了常见收集操作的实现。
:返回一个流,其中包含将给定收集器应用于该流的元素的结果。
返回一个 Gatherer,它将元素收集到给定大小的窗口(遇到有序的元素组)中,其中每个后续窗口都包含前一个窗口的所有元素(除了最近的元素之外),并在流中添加下一个元素。 […]
示例:
// will contain: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8]] List<List<Integer>> windows2 = Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(2)).toList(); // will contain: [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]] List<List<Integer>> windows6 = Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(6)).toList();