web-dev-qa-db-ja.com

Java 8のScalaのfoldLeftと同等

Scalaの素晴らしいfoldLeft in Java 8と同等のものは何ですか?

私はそれがreduceであると考えたがりましたが、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();
    }
}
_

上記のコードの問題はaccumulatorです:new StringBuilder("")

したがって、誰かが私にfoldLeft /の適切な同等物を指し示すことができますか?私のコードを修正しますか?

19
GA1

更新:

コードを修正する最初の試みは次のとおりです。

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

次の reduceメソッド を使用します。

<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 を確認してください。

ただし、アキュムレータがであるべきであるreduceの規約に違反しているため、この使用法は正しくありません(= /// =)、結果に追加要素を組み込むための連想、非干渉、ステートレス関数です。言い換えると、IDは変更可能であるため、並列実行の場合、結果は壊れます。

以下のコメントで指摘されているように、正しいオプションは次のように削減を使用しています。

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

サプライヤーStringBuilder::newは、後で結合される再利用可能なコンテナを作成するために使用されます。

12
Lachezar Balev

Java 8のストリームAPIにfoldLeftに相当するものはありません。他の人が述べたように、reduce(identity, accumulator, combiner)は近くにありますが、foldLeft結果の型Bがそれ自体と結合し、すべての型が持つわけではないプロパティである連想的(言い換えれば、モノイドのようなもの)である必要があります。

このための機能強化リクエストもあります: add Stream.foldLeft()terminal operation

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
_

reduce(2, fun, combine)を記述しようとした場合、2つの数値を結合するどの結合関数を渡すことができますか? 2つの数字を一緒に追加しても明らかに解決しません。また、値_2_は明らかにidentity要素ではありません。

順次実行を必要とする操作は、reduceで表現できないことに注意してください。 foldLeftは実際にはreduceよりも汎用的です。reducefoldLeftを実装できますが、foldLeftreduceを実装することはできません。

16
dzs

探しているメソッドは Java.util.Stream.reduce 、特に、3つのパラメーター、identity、accumulator、およびbinary functionを含むオーバーロード。これは、ScalaのfoldLeftと同等の正しいものです。

ただし、Javaのreduceをそのまま使用することはnot許可されており、ScalaのfoldLeftも使用できません。代わりに collect を使用してください。

6
Jörg W Mittag

他のものは正しいが、同等のものはない。こちらがユーティリティです。

<U, T> U foldLeft(Collection<T> sequence, U identity, BiFunction<U, ? super T, U> accumulator) {
    U result = identity;
    for (T element : sequence)
        result = accumulator.apply(result, element);
    return result;
}

上記の方法を使用した場合は次のようになります-

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

または、ラムダメソッドref sugarなしで、

public String concatenate(List<Character> chars) {
    return foldLeft(chars, new StringBuilder(""), (stringBuilder, character) -> stringBuilder.append(character)).toString();
}
0
Gagandeep Kalra