2016-12-20 10 views
11

Scalaの偉大なfoldLeftのJava 8に相当するものは何ですか?JavaでScalaのfoldLeftに相当するもの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(); 
    } 
} 

問題がacc umulatorです:new StringBuilder("")

このように、誰もがfoldLeft /私のコードを修正する適切な同等に私を指すことができますか?

+2

FYI:言語の名前は「SCALA」ではなく「Scala」です。 (私はおそらくあなたが意味するものではない "スカラ"と呼ばれる別の言語があると信じています) –

+0

関連http://stackoverflow.com/questions/30736587/builder-pattern-with-a-java-8-stream – Tunaki

答えて

6

アップデート:ここで

はあなたのコードは、固定得るために最初の試みである:

public static String concatenate(List<Character> chars) { 
     return chars 
       .stream() 
       .reduce(new StringBuilder(), 
           StringBuilder::append, 
           StringBuilder::append).toString(); 
    } 

それは、次のreduce methodを使用しています:

<U> U reduce(U identity, 
       BiFunction<U, ? super T, U> accumulator, 
       BinaryOperator<U> combiner); 

それは混乱に聞こえるかもしれませんが、あなたが見ている場合javadocsには、詳細をすばやく把握するのに役立つ素晴らしい説明があります。減少は、次のコードと同等です:より詳細な説明については

U result = identity; 
for (T element : this stream) 
    result = accumulator.apply(result, element) 
return result; 

this sourceを確認してください。それはアキュムレータ結果に添加元素を組み込むため会合、非干渉、ステートレス関数でなければならないことを述べて減らすの契約に違反しているため

この使用は、しかし正しくありません。言い換えれば、アイデンティティが変更可能であるため、パラレル実行の場合に結果が破損します。

正しいオプション以下のコメントで指摘したように、次のように還元を使用している:

return chars.stream().collect(
    StringBuilder::new, 
    StringBuilder::append, 
    StringBuilder::append).toString(); 

サプライヤーStringBuilder::new、後で組み合わされる再利用可能なコンテナを作成するために使用されるであろう。

+6

他の答えと同じです:*このように 'reduce'を使わないでください。これらの関数はパラメータを変更することはできません。正しい使用法は '.collect(StringBuilder :: new、StringBuilder :: append、StringBuilder :: append)'です。 [Mutable reduction](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#MutableReduction)を参照してください。 – Holger

+0

@Holger:ありがとう、本当です。答えが更新されます。 –

+3

これは効率に関するものではなく、正確さに関するものです。このように 'reduce 'を使うと、契約に違反し、特定の状況下で意図したことをしても、壊れているとみなされなければならない。特に、並列ストリームを使用している場合は、必ず破棄されます。 – Holger

7

探している方法は、java.util.Stream.reduceです。特に、3つのパラメータ、ID、アキュムレータ、およびバイナリ関数を持つオーバーロードがあります。これはScalaのfoldLeftに相当する正しいものです。

では、Javaのreduceをそのまま使用できますが、ScalaのfoldLeftでは使用できません。代わりにcollectを使用してください。

+3

私はあなたの答えが好きですが、 "あなたは許されていません"と少し間違っているようです。あなたはそれを言い換えることができますか? –

+2

Javaの型システムがその制約を表現するのに十分な表現力を持っていれば、型エラーになります。しかし、そうではないので、制約はJavaDocsでのみ言及されています。 JavaDocsはあなたが渡すことができるオブジェクトの種類を示し、OPが渡すオブジェクトはそれらの制約を満たさないため、彼女は 'reduce'を呼び出すことはできません。それ以外はどうやって文句しますか? –

+2

さて、タイプの制限はなく、オブジェクトの使い方にのみ制限があります。 '(a、b) - > new StringBuilder()。append(a).append(b)'のようなアキュムレータとコンバイナ関数を使用すると、 'collect'メソッドに比べて効率的ではありませんが、溶液。 – Holger

7

Java 8のストリームAPIには、foldLeftと同等のものはありません。他の人が指摘しているように、reduce(identity, accumulator, combiner)は近づいていますが、結果のタイプがBであり、すべてのタイプではないプロパティである(他の言い方をすればモノヨンのような)結合が必要なため、foldLeftと等価ではありません。

このため拡張要求もあります:

add Stream.foldLeft() terminal operation

削減が動作しない理由を参照するには、指定した番号から始まる算術演算のシリーズを実行しようとする次のコードを、検討し、

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 

reduce(2, fun, combine)を書こうとした場合、2つの数値を組み合わせたコンバイナ関数を渡すことはできますか? 2つの数字を一緒に追加してもそれを解決することはできません。また、値2は、明らかにアイデンティティ要素ではありません。

逐次実行を必要とする操作は、reduceで表すことはできません。 foldLeftは、実際にはreduceより一般的です。reducefoldLeftと実装できますが、をreduceで実装することはできません。

関連する問題