是否可以只迭代一次流并执行 2 个或更多操作?
Is it possible to iterate a stream only once and perform 2 or more operations?
给定代码
List<Integer> numbers = Arrays.asList(2, 4, 3);
int sumTotal = numbers.stream().reduce(-3, (x, y) -> x + y + 3);
int multiplyTotal = numbers.stream().reduce(1, (x, y) -> x * y);
是否可以在仅迭代流一次的同时执行这两个操作?
另外,请注意每个 reduce 都有不同的标识:-3 和 1。
在 JS 中,一种解决方案是遍历对象而不是数字。例如:
numbers.stream().reduce({sum:0,prod:1}, (memo, cur) ->
memo.sum = memo.sum + cur + 3;
memo.prod = memo.prod * cur;
return memo;
)
请注意,在您的示例中,x
是 "memo"(或累加器),y
是当前值。
同样的想法也应该适用于 Java。
您可以创建自定义 class,并使用可变缩减:
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(2, 4, 3);
Result result = numbers.stream().collect(Result::new, Result::consume, Result::combine);
System.out.println("result = " + result);
}
private static class Result {
private int sum = 0;
private int product = 1;
public void consume(int i) {
sum += i + 3;
product *= i;
}
public void combine(Result r) {
// READ note below
sum += r.sum + 3;
product *= r.product;
}
@Override
public String toString() {
return "Result{" +
"sum=" + sum +
", product=" + product +
'}';
}
}
但我怀疑你这样做是否会比你正在做的简单地迭代两次获得更多。
编辑:
请注意,上面的 combine() 方法在使用并行流时调用,将产生与顺序流不一致的结果。正如 Holger 在评论中提到的那样,问题是由于该操作不遵守归约的身份先决条件这一事实引起的:对具有身份的任何值 V 应用归约应该产生 V,但它产生 V + 3 以 0 作为标识。
确保不要使用并行流来计算该结果。
你可以使用我在 中写的配对收集器:
int[] result = numbers.stream().collect(
pairing(
Collectors.reducing(0, (x, y) -> x + y + 3),
Collectors.reducing(1, (x, y) -> x * y),
(sum, prod) -> new int[] { sum, prod }));
当然,您可以通过任何其他方式合并结果。将它们放入数组只是一个例子。
这个收集器在我的 StreamEx library (MoreCollectors.pairing). Alternatively you may use jOOL library: it has Tuple.collectors 方法中很容易获得,它以类似的方式工作。
给定代码
List<Integer> numbers = Arrays.asList(2, 4, 3);
int sumTotal = numbers.stream().reduce(-3, (x, y) -> x + y + 3);
int multiplyTotal = numbers.stream().reduce(1, (x, y) -> x * y);
是否可以在仅迭代流一次的同时执行这两个操作?
另外,请注意每个 reduce 都有不同的标识:-3 和 1。
在 JS 中,一种解决方案是遍历对象而不是数字。例如:
numbers.stream().reduce({sum:0,prod:1}, (memo, cur) ->
memo.sum = memo.sum + cur + 3;
memo.prod = memo.prod * cur;
return memo;
)
请注意,在您的示例中,x
是 "memo"(或累加器),y
是当前值。
同样的想法也应该适用于 Java。
您可以创建自定义 class,并使用可变缩减:
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(2, 4, 3);
Result result = numbers.stream().collect(Result::new, Result::consume, Result::combine);
System.out.println("result = " + result);
}
private static class Result {
private int sum = 0;
private int product = 1;
public void consume(int i) {
sum += i + 3;
product *= i;
}
public void combine(Result r) {
// READ note below
sum += r.sum + 3;
product *= r.product;
}
@Override
public String toString() {
return "Result{" +
"sum=" + sum +
", product=" + product +
'}';
}
}
但我怀疑你这样做是否会比你正在做的简单地迭代两次获得更多。
编辑:
请注意,上面的 combine() 方法在使用并行流时调用,将产生与顺序流不一致的结果。正如 Holger 在评论中提到的那样,问题是由于该操作不遵守归约的身份先决条件这一事实引起的:对具有身份的任何值 V 应用归约应该产生 V,但它产生 V + 3 以 0 作为标识。
确保不要使用并行流来计算该结果。
你可以使用我在
int[] result = numbers.stream().collect(
pairing(
Collectors.reducing(0, (x, y) -> x + y + 3),
Collectors.reducing(1, (x, y) -> x * y),
(sum, prod) -> new int[] { sum, prod }));
当然,您可以通过任何其他方式合并结果。将它们放入数组只是一个例子。
这个收集器在我的 StreamEx library (MoreCollectors.pairing). Alternatively you may use jOOL library: it has Tuple.collectors 方法中很容易获得,它以类似的方式工作。