GA1*_*GA1 22 java reduce java-8 foldleft
Scala foldLeft在Java 8中的优势是什么?
我很想想它reduce,但是减少必须返回与它减少的相同类型的东西.
例:
import java.util.List;
public class Foo {
// this method works pretty well
public int sum(List<Integer> numbers) {
return numbers.stream()
.reduce(0, (acc, n) -> (acc + n));
}
// this method makes the file not compile
public String concatenate(List<Character> chars) {
return chars.stream()
.reduce(new StringBuilder(""), (acc, c) -> acc.append(c)).toString();
}
}
Run Code Online (Sandbox Code Playgroud)
上面代码中的问题是accumulator:new StringBuilder("")
因此,任何人都可以指出我正确的foldLeft/修复我的代码?
dzs*_*dzs 18
foldLeft在Java 8的Stream API中没有相应的东西.正如其他人所指出的那样,reduce(identity, accumulator, combiner)它是接近的,但它并不等同,foldLeft因为它要求结果类型B与自身结合并且是关联的(换句话说,类似于monoid),这是一种并非每种类型都具有的属性.
还有一个增强请求:添加Stream.foldLeft()终端操作
要了解为什么reduce不起作用,请考虑以下代码,您打算从给定数字开始执行一系列算术运算:
val arithOps = List(('+', 1), ('*', 4), ('-', 2), ('/', 5))
val fun: (Int, (Char, Int)) => Int = {
case (x, ('+', y)) => x + y
case (x, ('-', y)) => x - y
case (x, ('*', y)) => x * y
case (x, ('/', y)) => x / y
}
val number = 2
arithOps.foldLeft(number)(fun) // ((2 + 1) * 4 - 2) / 5
Run Code Online (Sandbox Code Playgroud)
如果你尝试写作reduce(2, fun, combine),你可以通过哪种组合函数组合两个数字?将两个数字加在一起显然无法解决问题.此外,该值2显然不是一个标识元素.
请注意,不需要按顺序表示需要顺序执行的操作reduce.foldLeft实际上是更通用比reduce:你可以实现reduce用foldLeft,但你无法实现foldLeft用reduce.
Lac*_*lev 13
更新:
以下是修复代码的初步尝试:
public static String concatenate(List<Character> chars) {
return chars
.stream()
.reduce(new StringBuilder(),
StringBuilder::append,
StringBuilder::append).toString();
}
Run Code Online (Sandbox Code Playgroud)
它使用以下reduce方法:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
Run Code Online (Sandbox Code Playgroud)
这可能听起来令人困惑,但如果你看一下javadocs,有一个很好的解释可以帮助你快速掌握细节.减少量相当于以下代码:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
Run Code Online (Sandbox Code Playgroud)
有关更深入的说明,请查看此来源.
这种用法是不正确的,因为它违反了reduce的约定,它指出累加器应该是一个关联的,非干扰的,无状态的函数,用于将一个额外的元素合并到一个结果中.换句话说,由于身份是可变的,因此在并行执行的情况下将破坏结果.
正如下面的评论所指出的,正确的选择是使用如下减少:
return chars.stream().collect(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append).toString();
Run Code Online (Sandbox Code Playgroud)
供应商StringBuilder::new将用于创建可重复使用的容器,稍后将合并.
您正在寻找的方法java.util.Stream.reduce,特别是具有三个参数的重载,标识,累加器和二进制函数.这是Scala的正确等价物foldLeft.
但是,你是不是允许使用Java的reduce这种方式,也没有Scala的foldLeft对这一问题.请collect改用.
可以通过使用收集器来完成:
public static <A, B> Collector<A, ?, B> foldLeft(final B init, final BiFunction<? super B, ? super A, ? extends B> f) {
return Collectors.collectingAndThen(
Collectors.reducing(Function.<B>identity(), a -> b -> f.apply(b, a), Function::andThen),
endo -> endo.apply(init)
);
}
Run Code Online (Sandbox Code Playgroud)
使用示例:
IntStream.rangeClosed(1, 100).boxed().collect(foldLeft(50, (a, b) -> a - b)); // Output = -5000
Run Code Online (Sandbox Code Playgroud)
对于你的问题,这符合你的要求:
public String concatenate(List<Character> chars) {
return chars.stream()
.collect(foldLeft(new StringBuilder(), StringBuilder::append)).toString();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15208 次 |
| 最近记录: |