run*_*ror 11 java java-8 java-stream
我有一个整数列表,说list1,我想得到另一个列表list2,它将包含从开始到当前索引为止的累积总和。如何使用Stream API Java 8做到这一点?
List<Integer> list1 = new ArrayList<>();
list1.addAll(Arrays.asList(1, 2, 3, 4));
List<Integer> list2 = new ArrayList<>();
// initialization
list2.add(list1.get(0));
for(int i=1;i<list1.size();i++) {
// increment step
list2.add(list2.get(i-1) + list1.get(i));
}
Run Code Online (Sandbox Code Playgroud)
如何将上述命令式代码转换为声明式代码?
list2 should be [1, 3, 6, 10]
Run Code Online (Sandbox Code Playgroud)
Fed*_*ner 12
流不适合此类任务,因为其中涉及状态(累积的部分和)。相反,您可以使用Arrays.parallelPrefix:
Integer[] arr = list1.toArray(Integer[]::new);
Arrays.parallelPrefix(arr, Integer::sum);
List<Integer> list2 = Arrays.asList(arr);
Run Code Online (Sandbox Code Playgroud)
这是第list1一个使用Collection.toArray,从JDK 11开始可用的,将其复制到数组的方法。如果您尚未使用Java 11,则可以用传统toArray调用替换第一行:
Integer[] arr = list1.toArray(new Integer[0]);
Run Code Online (Sandbox Code Playgroud)
该解决方案不使用流,而是声明性的,因为它将Arrays.parallelPrefix累积操作作为参数接收(Integer::sum在这种情况下)。
时间复杂度为O(N),尽管与建立并行处理所需的基础结构可能涉及一些非微小的固定成本。但是,根据文档:
对于大型数组,并行前缀计算通常比顺序循环更有效
因此,似乎值得尝试这种方法。
另外,值得一提的是,这种方法之所以有效,Integer::sum是因为它是一种关联操作。这是一个要求。
对于每个索引:从零迭代到该索引,获取每个元素,然后获取和将
Box中的int Integers
收集到列表
IntStream.range(0, list1.size())
.map(i -> IntStream.rangeClosed(0, i).map(list1::get).sum())
.boxed()
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
您每次都将每个数字加在一起,而不是重复使用以前的累积结果,但是流不适合查看先前迭代的结果。
您可以编写自己的收集器,但是到现在为止,老实说,您为什么还要烦扰流?
list1.stream()
.collect(
Collector.of(
ArrayList::new,
(a, b) -> a.add(a.isEmpty() ? b : b + a.get(a.size() - 1)),
(a, b) -> { throw new UnsupportedOperationException(); }
)
);
Run Code Online (Sandbox Code Playgroud)
一个O(n)(仅按顺序工作)解决方案如下,但我觉得它不是很优雅。我想这是一个品味问题
AtomicInteger ai = new AtomicInteger();
List<Integer> collect = list1.stream()
.map(ai::addAndGet)
.collect(Collectors.toList());
System.out.println(collect); // [1, 3, 6, 10]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1213 次 |
| 最近记录: |