foldLeft和Java的collect之间有什么区别?

huangapple go评论36阅读模式
英文:

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:

  •  &lt;U&gt; U foldLeft(U zero,
                    BiFunction&lt;? super U, ? super T, ? extends U&gt; combiner)
    

and Java's collect:

  • &lt;R&gt; R collect(Supplier&lt;R&gt; supplier,
                  BiConsumer&lt;R, ? super T&gt; accumulator,
                  BiConsumer&lt;R, R&gt; combiner)
    

It seems both can achieve the same purpose: Transform a collection (say Stream&lt;T&gt;) 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 = &quot;HelloFunctionalWorld&quot;
String result = Seq(&quot;Hello&quot;, &quot;Functional&quot;, &quot;World&quot;)
    .foldLeft(new StringBuilder(), (acc, word) -&gt; acc.append(word))
    .toString();

Java collect:

// result = &quot;HelloFunctionalWorld&quot;
final String result = Arrays.asList(&quot;Hello&quot;, &quot;Functional&quot;, &quot;World&quot;).stream()
    .collect(
        StringBuilder::new,
        (acc, word) -&gt; 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

Java Stream

英文:

> Which are the actual differences conceptually?

As the javadoc of collect(Supplier&lt;R&gt; supplier, BiConsumer&lt;R,? super T&gt; accumulator, BiConsumer&lt;R,R&gt; 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
&gt; R result = supplier.get();
&gt; for (T element : this stream)
&gt; accumulator.accept(result, element);
&gt; return result;
&gt;

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
&lt;U&gt; U foldLeft​(U zero,
               BiFunction&lt;? super U,​? super T,​? extends U&gt; combine)

// Java Stream
&lt;U&gt; U reduce(U identity,
             BiFunction&lt;U,? super T,U&gt; accumulator,
             BinaryOperator&lt;U&gt; 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&lt;? super T,​? super T,​? extends T&gt; combine)

// Java Stream
T reduce(T identity,
         BinaryOperator&lt;T&gt; accumulator)

UPDATE Comparison of the methods:

Vavr

Java Stream

huangapple
  • 本文由 发表于 2020年10月24日 02:42:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/64505699.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定