31
60分で体験する Stream / Lambda ハンズオン

60分で体験する Stream / Lambda ハンズオン

Embed Size (px)

Citation preview

Page 1: 60分で体験する Stream / Lambda ハンズオン

60分で体験するStream / Lambdaハンズオン

Page 2: 60分で体験する Stream / Lambda ハンズオン

gishi_yama• 某地方大学でeラーニングなどの情報システムの開発・研究に従事

• Wicket-Sapporo, Java Do ← New!

• Wicket User Guideの翻訳にチャレンジ中https://github.com/wicket-sapporo/wicket_user_guide_jp

Page 3: 60分で体験する Stream / Lambda ハンズオン

ハンズオンの概要Java 8のリリースから1年あまりが経過し、Stream APIやLambda 式が開発環境に導入される事例も次第に増えてきていることと思います。

Stream APIやLamda式のしくみ・書き方・ルールの基本と、これらを用いて既存のJavaコードをどう変化させることができるのか、体験してみましょう。

3

Page 4: 60分で体験する Stream / Lambda ハンズオン

練習・演習プロジェクト

https://github.com/java-­‐do/20150823_seminar

1. ‘Download Zip’ をして展開(もしくはgitでclone) 2. stream20150823 フォルダをインポート Eclipse:Existing Maven Project(既存のMavenプロジェクト) NetBeans:プロジェクトを開く

3. 指示に従ってHandsOn.javaを編集し、ファイルを実行する 4. 解答例は Answer.java に記載されている

Page 5: 60分で体験する Stream / Lambda ハンズオン

Stream / LambdaJavaに細かい並列処理を導入するため、Java8から導入されたAPIや記法のこと

• Stream:配列、List、Set、Mapといったコレクション的なオブジェクトへの順次・並列処理を記述するAPI

• Lambda:匿名の関数オブジェクトを作るための関数型インターフェースの実装(とその記法)

5

Page 6: 60分で体験する Stream / Lambda ハンズオン

6

ages 22 10 19 38

たとえば、配列を表示する

‣ ages を要素数分繰り返す ‣ iを要素番号として、agesから要素を取り出す ‣ 取り出した要素を表示する

int[]  ages  =  new  int[]{22,  10,  19,  38};      for  (int  i  =  0;  i  <  ages.length;  i++)  {              System.out.println(ages[i]);  }

Page 7: 60分で体験する Stream / Lambda ハンズオン

7

ages 22 10 19 38

たとえば、配列を表示する

Stream

int[]  ages  =  new  int[]{22,  10,  19,  38};      Arrays.stream(ages)      .forEach(n  -­‐>  System.out.println(n));

‣ agesをstreamにする ‣ streamの要素nをSystem.out.println(n)で表示する

Page 8: 60分で体験する Stream / Lambda ハンズオン

8

‣ ages を要素数分繰り返す ‣ iを要素番号として、agesから要素を取り出す ‣ 取り出した要素を表示する

‣ agesをstreamにする ‣ streamの要素nをSystem.out.println(n)で表示する

Page 9: 60分で体験する Stream / Lambda ハンズオン

ages 22 10 19 38

たとえば、選んで表示する

‣ ages を要素数分繰り返す ‣ iを要素番号として、agesから要素:nを取り出す ‣ nが20以上の時だけ、下を実行する ‣ nを表示する

※20以上を表示int[]  ages  =  new  int[]{22,  10,  19,  38};      for  (int  i  =  0;  i  <  ages.length;  i++)  {      int  n  =  ages[i];      if  (n  >=  20)  {          System.out.println(n);      }  }

Page 10: 60分で体験する Stream / Lambda ハンズオン

10

ages 22 10 19 38

たとえば、選んで表示する

Stream

‣ argsをStreamにする ‣ streamの要素nが20以上のものだけにフィルタする ‣ 残った要素nを表示する

※20以上を表示

int[]  ages  =  new  int[]{22,  10,  19,  38};      Arrays.stream(ages)              .filter(n  -­‐>  n  >=  20)  //  22,  38のみのStreamができる              .forEach(n  -­‐>  System.out.println(n));

Page 11: 60分で体験する Stream / Lambda ハンズオン

11

‣ ages を要素数分繰り返す ‣ iを要素番号として、agesから要素:nを取り出す ‣ nが20以上の時だけ、下を実行する ‣ nを表示する

‣ argsをStreamにする ‣ streamの要素nが20以上のものだけにフィルタする ‣ 残った要素nを表示する

Page 12: 60分で体験する Stream / Lambda ハンズオン

練習1を やってみましょう

12

Page 13: 60分で体験する Stream / Lambda ハンズオン

13

Streamの構造int[]  ages  =  new  int[]{22,  10,  19,  38};  

Arrays.stream(ages)  

           .filter(n  -­‐>  n  >=  20)

           .forEach(n  -­‐>  System.out.println(n));  

‣ 終端操作が実行されるときに、パイプラインの全てが実行される ‣ それぞれにどのような種類があるのかは@hisidamaさんのhttp://www.ne.jp/asahi/hishidama/home/tech/java/stream.html#h_method がわかりやすい

ストリーム・パイプライン

Page 14: 60分で体験する Stream / Lambda ハンズオン

14

ages 22 10 19 38

並列処理

Stream※20以上を表示

int[]  ages  =  new  int[]{22,  10,  19,  38};      Arrays.stream(ages)              .parallel()              .filter(n  -­‐>  n  >=  20)                .forEach(n  -­‐>  System.out.println(n));

streamをparallel streamに変化させるだけで、並列処理になる ※順序保証だとパフォーマンスは下がる

Page 15: 60分で体験する Stream / Lambda ハンズオン

15

ages 22 10 19 38

要素の入れ替え

Stream

map、mapToXXというメソッドで、要素を入れ替えた Streamを生成し、処理を継続する

Arrays.stream(ages)              .filter(n  -­‐>  n  >=  20)  //  22,  38のみのStreamができる              .mapToObj(n  -­‐>  Integer.toBinaryString(n))                  //  2進数化したStreamができる              .forEach(n  -­‐>  System.out.println(n));

Page 16: 60分で体験する Stream / Lambda ハンズオン

16

Lambda式

・n  -­‐>  n  >=  20  

・n  -­‐>  Integer.toBinaryString(n)  

・n  -­‐>  System.out.println(n)Streamの中に現れた

これらが Lambda(ラムダ)式。

ラムダ式は、”式”の様に処理(関数)を書ける 「関数オブジェクト」

Javaでは、関数型インターフェースの無名クラスが原型

Page 17: 60分で体験する Stream / Lambda ハンズオン

無名クラスインスタンス化の際に{}をつけることで、サブクラスやインターフェースの実装クラスのオブジェクトをその場で作ることができる。このサブクラスには、クラス名がない。 この手法(と、作られたクラス)を無名クラスという。

public  class  GamePlayer  extends  AbstractPlayer  {      //  AbstractPlayerのサブクラス    //  このサブクラスには GamePlayer というクラス名前がある  }

AbstractPlayer  player  =  new  AbstractPlayer()  {      //  このオブジェクトは、AbstractPlayerのサブクラスのオブジェクトとなる    //  サブクラスにはクラス名が存在しないので、無名クラスと呼ばれる  

};

↓通常のサブクラス↑無名クラス

Page 18: 60分で体験する Stream / Lambda ハンズオン

関数型インターフェース

18

Page 19: 60分で体験する Stream / Lambda ハンズオン

int[]  ages  =  new  int[]{22,  10,  19,  38};  

IntConsumer  consumer  =  new  IntConsumer()  {  //  Consumerの無名クラス  

   @Override      public  void  accept(int  n)  {          System.out.println(n);      }  };      Arrays.stream(ages)              .forEach(consumer); 

例:配列を表示する終端操作の Consumerの無名クラス

Page 20: 60分で体験する Stream / Lambda ハンズオン

int[]  ages  =  new  int[]{22,  10,  19,  38};  

IntConsumer  consumer  =  new  IntConsumer()  {  //  Consumerの無名クラス  

   @Override      public  void  accept(int  n)  {          System.out.println(n);      }  };      Arrays.stream(ages)              .forEach(consumer); 

無名クラスからラムダ式へ

Page 21: 60分で体験する Stream / Lambda ハンズオン

int[]  ages  =  new  int[]{22,  10,  19,  38};  

IntConsumer  consumer  =    n  -­‐>  System.out.println(n);      

Arrays.stream(ages)              .forEach(consumer); 

ラムダ式

Page 22: 60分で体験する Stream / Lambda ハンズオン

int[]  ages  =  new  int[]{22,  10,  19,  38};  

IntConsumer  consumer  =    n  -­‐>  System.out.println(n);  

Arrays.stream(ages)              .forEach(n  -­‐>  System.out.println(n)); 

引数に直接渡す

Page 23: 60分で体験する Stream / Lambda ハンズオン

アロー演算子の左辺が複数行になるときは{}で囲むint[]  ages  =  new  int[]{22,  10,  19,  38};  

Arrays.stream(ages)      .forEach(n  -­‐>  {          String  s  =  n  +  "歳";          System.out.println(s);      });  

Page 24: 60分で体験する Stream / Lambda ハンズオン

練習2 演習1~10を やってみましょう

24

Page 25: 60分で体験する Stream / Lambda ハンズオン

List<String>  choiced  =  profiles.stream()      .filter(p  -­‐>  p.isFamale())      .filter(p  -­‐>  p.isAdult())      .map(p  -­‐>  p.getName())      .forEach(i  -­‐>  System.out.println(i));

List<String>  choiced  =  profiles.stream()      .filter(Profile::isFamale)      .filter(Profile::isAdult)      .map(Profile::getName)      .forEach(System.out::println);

メソッド参照

ラムダ式の部分を オブジェクト::メソッド名 クラス::メソッド名 に書き換えられる(引数も推測される)

Page 26: 60分で体験する Stream / Lambda ハンズオン

副作用は(極力)避けるStringJoiner  joiner  =  new  StringJoiner(",",  "Profile{",  "}");  Stream.of(name,  sex.toString(),  age.toString(),  state,  policy)      .forEach(joiner::add);

StringJoiner  joiner  =  new  StringJoiner(",",  "Profile{",  "}");  Stream.of(name,  sex.toString(),  age.toString(),  state,  policy)      .parallel()      .forEach(joiner::add);

joiner に変化(副作用)を与えるStream 上(順次処理)と下(並列処理)で結果が異なる! やむをえない場合もあるが、副作用のある処理は極力さけることで、 エラーの防止や並列化をしやすくなる

※なお、ラムダ式の名で呼び出される変数は暗黙的にfinalとなる (上の例では、joinerがfinalとして扱われる)

Page 27: 60分で体験する Stream / Lambda ハンズオン

まとめ

27

Page 28: 60分で体験する Stream / Lambda ハンズオン

まとめStream:  コレクション的なオブジェクトへの順次・並列処理をパイプラインで記述できる  

Lambda:  関数型インターフェースの実装(Stream内で処理させたい内容など)を式やメソッド参照で書ける  

Stream  API  /  Lambdaを使うことで、for文を使う処理をより明確に・簡素に書ける  

streamをparallel化するだけで並列処理を行わせることができるようになる(副作用の有無に注意)

Page 29: 60分で体験する Stream / Lambda ハンズオン

Wicketも将来的には…

Martijn Dashorst(2015). Apache Wicket 10 years beyond

Page 30: 60分で体験する Stream / Lambda ハンズオン

参考文献・Javaによる関数型プログラミング(ISBN:4873117046) ・Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング (ISBN:4844336673)

Page 31: 60分で体験する Stream / Lambda ハンズオン

参考文献・きつねさんと学ぶ Lambda式&StreamAPIハンズオン http://www.slideshare.net/bitter_fox/handson-50579009https://bitbucket.org/bitter_fox/lambda.git

・ひしだまのホームページ:Java Stream http://www.ne.jp/asahi/hishidama/home/tech/java/stream.html

・Java8のstreamを使いこなす:きしだのはてな http://d.hatena.ne.jp/nowokay/20130504

・JavaのStreamで学ぶ遅延処理実装パターン http://www.slideshare.net/mikeneck/javastream

・社内Java8勉強会 ラムダ式とストリームAPI http://www.slideshare.net/zoetrope/java8-lambdaandstream

・Apache Wicket 10 years beyond http://www.slideshare.net/dashorst/wicket-10-years-and-beyond