英文:
What's the difference b/w foldLeft and Java collect
问题
这个问题针对 Vavr(Java 的函数式编程库)提出,但可能也适用于 Scala 或其他与 Java 相媲美的函数式编程语言。
Vavr 的 foldLeft
与 Java 的 collect
之间有什么区别:
Vavr foldLeft
:
<U> U foldLeft(U zero,
BiFunction<? super U, ? super T, ? extends U> combiner)
和 Java 的 collect
:
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner)
看起来两者都可以实现相同的目标:通过遍历集合(比如 Stream<T>
)并在遍历过程中将结果累积到新类型 U
(或 R
)中。
乍一看,函数签名在参数数量上有所不同,并且 Java 的 collect
似乎是为了在并行块中运行而设计的。
实际上在概念上有哪些真正的区别? Vavr 的 foldLeft
看起来更容易使用。
以下是一个简单示例,仅用于说明:
Vavr 的 foldLeft
:
// result = "HelloFunctionalWorld"
String result = Seq.of("Hello", "Functional", "World")
.foldLeft(new StringBuilder(), (acc, word) -> acc.append(word))
.toString();
Java 的 collect
:
// result = "HelloFunctionalWorld"
final String result = Arrays.asList("Hello", "Functional", "World").stream()
.collect(
StringBuilder::new,
(acc, word) -> acc.append(word),
StringBuilder::append
)
.toString();
英文:
This question is targeted to Vavr (Java's FP library), but could probably apply to Scala or other FP languages that can compare to Java.
What's the difference between Vavr foldLeft
:
-
<U> U foldLeft(U zero, BiFunction<? super U, ? super T, ? extends U> combiner)
and Java's collect
:
-
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)
It seems both can achieve the same purpose: Transform a collection (say Stream<T>
) by traversing it and accumulating a result into a new type U
(or R
) while traversing.
At first sight, signatures differ in #arguments, and Java collect
seems to be intended to be run in parallel chunks.
Which are the actual differences conceptually? Vavr foldLeft
seems easier to use.
Very dumb example just to illustrate:
Vavr foldLeft
:
// result = "HelloFunctionalWorld"
String result = Seq("Hello", "Functional", "World")
.foldLeft(new StringBuilder(), (acc, word) -> acc.append(word))
.toString();
Java collect
:
// result = "HelloFunctionalWorld"
final String result = Arrays.asList("Hello", "Functional", "World").stream()
.collect(
StringBuilder::new,
(acc, word) -> acc.append(word),
StringBuilder::append
)
.toString();
答案1
得分: 4
以下是翻译好的内容:
从概念上讲,实际上有哪些区别?
如
collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
的Javadoc 所述:对此流的元素执行可变减少操作。可变减少是一种减少操作,其中减少后的值是可变结果容器(例如
ArrayList
),并且通过更新结果的状态而不是替换结果来合并元素。这会产生等效于:R result = supplier.get(); for (T element : this stream) accumulator.accept(result, element); return result;
这里的关键短语是 mutable,因为Vavr 不执行任何可变操作,它是一个函数式库。
在问题中使用
StringBuilder
的示例实际上违反了函数式原则。如果您不打算遵循函数式范 paradigms 为什么使用 Vavr?
foldLeft()
的Java Stream 等效方法是reduce()
,您是正确的,第三个参数是为了支持并行处理:// Vavr <U> U foldLeft(U zero, BiFunction<? super U,? super T,? extends U> combine) // Java Stream <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
如果结果类型与流元素类型相同,您将使用以下替代方法,它们不需要第三个参数来支持并行处理:
// Vavr T fold(T zero, BiFunction<? super T,? super T,? extends T> combine) // Java Stream T reduce(T identity, BinaryOperator<T> accumulator)
更新 方法的比较:
Vavr
T fold(T zero, BiFunction<? super T,? super T,? extends T> combine)
以任意顺序应用
combine
,因此它必须是一个关联函数。
<U> U foldLeft(U zero, BiFunction<? super U,? super T,? extends U> combine)
从左到右应用值。
在顺序模式下等效于
reduce(identity, accumulator, combiner)
,其中未使用combiner
。通过有效的combiner
函数支持并行模式。
<U> U foldRight(U zero, java.util.function.BiFunction<? super T,? super U,? extends U> combine)
从右到左应用值。
没有Java Stream 等效方法。
Java Stream
Optional<T> reduce(BinaryOperator<T> accumulator)
以任意顺序应用
accumulator
,因此它必须是一个关联函数。例如,使用+
处理1,2,3,4
可以被处理为((1+2)+3)+4
(顺序模式),或(1+2)+(3+4)
,1+((2+3)+4)
或任何顺序组合(并行模式)。没有Vavr 等效方法。
T reduce(T identity, BinaryOperator<T> accumulator)
以任意顺序应用
accumulator
,因此它必须是一个关联函数。例如,使用0
和+
处理1,2,3,4
可以被处理为(((0+1)+2)+3)+4
(顺序模式),或((0+1)+2) + ((0+3)+4)
或任何顺序组合(并行模式)。等效于
fold(zero, combine)
。
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
按顺序应用值,
combiner
提供对并行处理的支持。顺序模式 等效于 foldLeft(zero, combine)`。
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
在顺序模式下,
supplier
用于创建可变结果容器,然后按顺序应用值。在并行模式中,创建多个可变结果容器并填充值,然后使用combiner
函数合并它们。没有Vavr 等效方法。
英文:
> Which are the actual differences conceptually?
As the javadoc of collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
says:
> Performs a mutable reduction operation on the elements of this stream. A mutable reduction is one in which the reduced value is a mutable result container, such as an ArrayList
, and elements are incorporated by updating the state of the result rather than by replacing the result. This produces a result equivalent to:
>
> lang-java
> R result = supplier.get();
> for (T element : this stream)
> accumulator.accept(result, element);
> return result;
>
The key phrase here is mutable, since Vavr doesn't do anything mutable, being a functional library.
The example in the question using StringBuilder
is actually a violation of the functional principles. Why use Vavr if you don't intend to follow the functional paradigms?
The Java Stream equivalent of foldLeft()
is reduce()
, and you're correct, the 3rd parameter is to support parallel processing:
// Vavr
<U> U foldLeft(U zero,
BiFunction<? super U,? super T,? extends U> combine)
// Java Stream
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
If the result type is the same as the stream element type, you would be using the following alternatives, which don't need a 3rd parameter for parallel processing support:
// Vavr
T fold(T zero,
BiFunction<? super T,? super T,? extends T> combine)
// Java Stream
T reduce(T identity,
BinaryOperator<T> accumulator)
UPDATE Comparison of the methods:
Vavr
-
T fold(T zero, BiFunction<? super T,? super T,? extends T> combine)
Applies
combine
in arbitrary order, so it must be an associative function.Equivalent to
reduce(identity, accumulator)
. -
<U> U foldLeft(U zero, BiFunction<? super U,? super T,? extends U> combine)
Applies values from left-to-right.
Equivalent to
reduce(identity, accumulator, combiner)
in sequential mode, wherecombiner
is unused. Parallel mode supported with a validcombiner
function. -
Applies values from right-to-left.
No Java Stream equivalent.
Java Stream
-
Optional<T> reduce(BinaryOperator<T> accumulator)
Applies
accumulator
in arbitrary order, so it must be an associative function. E.g. streaming1,2,3,4
with+
could be processed as((1+2)+3)+4
(sequential mode), or(1+2)+(3+4)
,1+((2+3)+4)
or any order combination (parallel mode).No Vavr equivalent.
-
T reduce(T identity, BinaryOperator<T> accumulator)
Applies
accumulator
in arbitrary order, so it must be an associative function. E.g. streaming1,2,3,4
with0
and+
could be processed as(((0+1)+2)+3)+4
(sequential mode), or((0+1)+2) + ((0+3)+4)
or any order combination (parallel mode).Equivalent to
fold(zero, combine)
. -
Applies values in order, with
combiner
providing support for parallel processing.Sequential mode is equivalent to foldLeft(zero, combine)`.
-
In sequential mode,
supplier
is used to create a mutable result container, and values are then applied in order. In parallel mode, multiple mutable result containers are created and filled with values, and are then merged using thecombiner
function.No Vavr equivalent.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论