Java関数式プログラミング(九)MapReduce

7024 ワード

map(マッピング)とreduce(帰約、簡略化)は数学上の2つの基礎的な概念であり、それらは早くから各種の関数プログラミング言語に現れ、2003年にGoogleがそれを発揚し、分布式システムに運用して並列計算を行った後、この組み合わせの名前はコンピュータ界で異彩を放ち始めた(それらの関数式粉はそうは思わないかもしれない)Java 8が関数プログラミングをサポートするようになった後、mapとreduceの組み合わせの初登場を見ることができます(ここでは初歩的な紹介にすぎませんが、後でそれらに関する特集もあります).
このシリーズの文章はVenkat Subramaniamの
Functional Programming in Java
未完は続きますので、後続の文章に引き続きご注目ください
Java翻訳ステーション .
集合を集約する
ここでは、一致する要素を検索したり、単一の要素を検索したり、変換したりする操作の新しいテクニックについて説明しました.これらの操作には、集合内の単一の要素を操作する共通点があります.要素を比較したり、2つの要素を演算したりする必要はありません.このセクションでは、要素を比較したり、セットを遍歴したりする方法について説明します.プロセス中に演算結果を動的に維持します.
最初の例では、friendsの集合を巡り、すべての名前の総文字数を計算します.

System.out.println("Total number of characters in all names: " + friends.stream()
         .mapToInt(name -> name.length())
         .sum());

すべての文字の総数を算出するには、各名前の長さを知る必要があります.mapToInt()メソッドで簡単にこれを完成できます.名前を対応する長さに変換した後、最後にそれらを1つ加えるだけでいいです.これを完成するには、内蔵のsum()メソッドがあります.次は最後の出力です.

Total number of characters in all names: 26 

map操作の変種であるmapToInt()メソッド(mapToInt,mapToDoubleなど)を用いて、IntStream,DoubleStreamなどの特定のタイプのストリームを生成し、返される長さに基づいて総文字数を計算します.
sum法のほかにもmax()で最大の長さを求めることができ、min()で最小の長さを求めることができ、sorted()で長さをソートすることができ、average()で平均の長さを求めることができるなど、似たような方法がたくさんあります.
上記の例のもう一つの魅力的な点は、現在ますます流行しているMapReduceモードであり、map()メソッドがマッピングされ、sum()メソッドは比較的一般的なreduceオペレーションである.実際、JDKにおけるsum()メソッドの実現にはreduce()メソッドが用いられる.reduceオペレーションのより一般的ないくつかの形式を見てみよう.
たとえば、私たちはすべての名前を巡り、一番長い名前を印刷します.一番長い名前がいくつかある場合は、最初に見つけたものを印刷します.1つの方法は、最大の長さを計算し、この長さに一致する最初の要素を選択することです.しかし、このようにするには、2回のリストを巡回する必要があります.効率が低すぎます.これはreduce操作です.出場の時だ.
2つの要素の長さをreduce操作で比較し、最も長い要素を返し、残りの要素とさらに比較することができます.前に見た他の高次関数と同じように、reduce()メソッドも同様にコレクション全体を遍歴しています.それ以外にlambda式が返す計算結果も記録されています.例があれば、この点をよりよく理解するのに役立ちます.まずコードを見てみましょう.

final Optional<String> aLongName = friends.stream()
         .reduce((name1, name2) ->
            name1.length() >= name2.length() ? name1 : name2);
aLongName.ifPresent(name ->
System.out.println(String.format("A longest name: %s", name)));

reduce()メソッドに渡されるlambda式は、2つのパラメータを受信します.name 1とname 2は、それらの長さを比較し、最も長いものを返します.reduce()メソッドは、私たちが何をするのか分かりません.この論理は、私たちが伝えたlambda式に剥離されます.これはポリシーモードの軽量レベルの実装です.
このlambda式はJDKのBinaryOperatorの関数インタフェースのapplyメソッドに適しています.これはreduceメソッドが受け入れるパラメータタイプです.このreduceメソッドを実行して、2つの最も長い名前の中で1つ目を正しく選択できるかどうかを見てみましょう.

A longest name: Brian

reduce()メソッドは、集合を巡回する過程で、まず集合の前の2つの要素に対してlambda式を呼び出し、呼び出しから返された結果は次の呼び出しに引き続き使用される.2回目の呼び出しでは、name 1の値は前回呼び出した結果にバインドされ、name 2の値は集合の3番目の要素である.残りの要素もこのように順次呼び出される.最後のlambda式呼び出しの結果は、reduce()メソッド全体が返した結果です.
reduce()メソッドは、その集合が空である可能性があるため、Optional値を返します.そうすると、最長の名前も存在しません.リストに要素が1つしかない場合、reduceメソッドはその要素を直接返し、lambda式を呼び出すことはありません.
この例から、reduceの結果は最大で1つの要素にすぎないと推定できます.デフォルト値またはベース値を返すことを望む場合は、reduce()メソッドの1つの変種を使用して、追加のパラメータを受信することができます.たとえば、最短の名前がSteveであれば、reduce()に渡すことができます.方法:

final String steveOrLonger = friends.stream()
     .reduce("Steve", (name1, name2) ->
            name1.length() >= name2.length() ? name1 : name2);

名前が長い場合は、この名前が選択されます.そうでない場合は、このベース値Steveが返されます.このバージョンのreduce()メソッドでは、集合が空の場合はデフォルト値が返されます.戻り値がない場合は考慮されません.
この章を終了する前に、集合操作の基礎的な操作を見てみましょう.要素をマージするのは簡単ではありません.
要素の結合
要素の検索、遍歴、および集合の変換方法について学習しました.しかし、この新しく追加されたjoin()がなければ、集合要素をつなぎ合わせる一般的な操作もあります.関数としては、前述の簡潔で優雅なコードは水の泡になるしかありません.この簡単な方法はJDKで最もよく使われる関数の一つになるほど実用的です.リストの要素をカンマで区切る方法を見てみましょう.
私たちはやはりこのfriendsリストを使います.JDKライブラリの古い方法であれば、すべての名前を印刷してカンマで区切るには、どんな仕事をしますか?
リストを巡り、要素を印刷しなければなりません.Java 5のforループは以前より改善されていますので、それを使いましょう.

for(String name : friends) {
      System.out.print(name + ", ");
}
System.out.println();

コードは簡単です.出力が何なのか見てみましょう.

Brian, Nate, Neal, Raju, Sara, Scott,

やばい、最後に嫌なカンマが増えた.Javaにカンマを置かないでくれないか.不幸なことに、ループは段階的に実行され、最後に特殊に処理させるのは容易ではない.この問題を解決するために、私たちは元のループに戻ることができる.

for(int i = 0; i < friends.size() - 1; i++) {
      System.out.print(friends.get(i) + ", ");
}
if(friends.size() > 0)
      System.out.println(friends.get(friends.size() - 1));

このバージョンの出力がOKかどうか見てみましょう.

Brian, Nate, Neal, Raju, Sara, Scott

結果は悪くないが、このコードはお世辞を言う勇気がない.助けて、Java.
Java 8のStringJoinerクラスは私たちにこれらの難題を解決してくれました.それだけでなく、Stringクラスはjoin方法を追加しました.私たちは上の塊の代わりに1行のコードを使うことができます.

System.out.println(String.join(", ", friends));

早く見てみましょう.結果はコードと同じように満足しています.

Brian, Nate, Neal, Raju, Sara, Scott

下位実装では、String.join()メソッドは、StringJoinerクラスを呼び出して、2番目のパラメータが入力された値(これは変長パラメータ)を長い文字列につなぎ、1番目のパラメータを区切り記号として使用します.このメソッドはカンマをつなぐことができるだけではありません.たとえば、パスをたくさん入力して、簡単にクラスパスをつづることができます.(classpath)、これは本当にこれらの新しく追加された方法とクラスのおかげです.
リスト要素を接続する方法はすでに知っています.リスト接続を行う前に、要素を変換することもできます.もちろんmapメソッドを使用してリスト変換を行う方法も知っています.次にfilter()を使用することもできます.方法は私たちが望んでいる要素をフィルタします.最後のステップの接続リスト要素は、カンマで区切ったものなのか、単純なreduce操作にすぎません.
要素をreduce()メソッドで文字列に結合することができますが、これには時間がかかります.JDKには便利なcollect()メソッドがあります.これもreduce()の変種であり、要素を所望の値に結合することができます.
collect()メソッドは、集約操作を実行しますが、特定の操作をcollectorに委任して実行します.変換された要素をArrayListにマージできます.先ほどの例に続いて、変換された要素をカンマで区切った文字列につづることができます.

System.out.println(
      friends.stream()
          .map(String::toUpperCase)
          .collect(joining(", ")));

変換後のリストでcollect()メソッドを呼び出し、joining()メソッドが返すcollectorに転送しました.joiningはCollectorsツールクラスの静的メソッドです.collectorは受信機のようなもので、collectが転送したオブジェクトを受信し、ArrayList、Stringなどのフォーマットで保存します.52ページの
collectメソッドおよびCollectorsクラスでは,このメソッドをさらに探索する.
これは出力された名前で、今は大文字でカンマで区切られています.

BRIAN, NATE, NEAL, RAJU, SARA, SCOTT

StringJoinerクラスは、集合接続のフォーマットをより柔軟に制御できます.接頭辞、接尾辞、または接尾辞を指定できます.どうでもいいです.
Lambda式と新しく追加された方法とクラスはJavaプログラミングをより簡単で快適にします.この章で何を話したかを振り返ってみましょう.
まとめ
集合はプログラミングでよく見られるもので、lambda式があるとJavaの集合操作がより簡単になります.ダラダラした集合操作の古いコードは、このような優雅で簡潔な新しい方法に変えることができます.内部反復器は集合を遍歴させ、変換をより便利にし、可変性の悩みから離れ、集合要素を探すのも異常に楽になります.これらの新しい方法を使用すると方法は多くのコードを書くことを少なくすることができます.これにより、コードのメンテナンスが容易になり、ビジネスロジックに焦点を当て、プログラミングにおける基本的な操作も少なくなります.
次の章では、lambda式がプログラム開発のもう一つの基本操作である文字列操作とオブジェクト比較を簡略化する方法について説明します.
未完は続きますので、後続の文章に引き続きご注目ください
Java翻訳ステーション .
オリジナル記事の転載は出典を明記してください.
http://it.deepinmind.com