のた犬のうまい猫めし

どら猫が作る、のた犬のための飯、略称、どら飯について語りつつ、各種技術、経済系セミナーに参加した報告、OSSいじってみた等のネタを入れていきます。更新情報はtwitterの@nota_inuにて。

Java女子部 Java8+ラムダ式ハンズオン勉強会(ラムダ式、stream) その1

ava8+ラムダ式ハンズオン(講師 櫻庭 祐一 様)  2014/10/11@青山オラクルセンター、に参加。

前置き

  • 櫻庭氏:ITproで詳説 JavaSE8連載を行っている。Google+のスイーツ王。
  • クロージャの原型。ラムダ式。ProjectLambda
  • Multi-Core Fine Grained Parallelism
  • Fork/Join Framework 。JavaSE7からあったが、使いづらい。
  • ITERATORをFork/Joinの上で動かす。
  • パラレル処理は、10万ほどの統計処理に使うと良い。(10程度では意味が無い。)

 【External Iterator

List<Integer> nums =...
for (Integer num: nums){
System.out.println(num);
}

 【Internal Iterator

List<Integer> nums= ...
nums.forEach(new Consumer<Integer>(){
public void accept(Integer num){
System.out.println(num);
}
});

 【Lambda】

List<Integer> nums=...
num.forEach(
num -> System.out.println(num));

  • 関数型言語の考えを取り入れる。Scalaの影響。便利。
  • Lambda for function、Stream API for Iterator
  • 匿名クラス(内部クラス AnonymousClass)を簡単に書く方法。
  • Functional Interface(関数型インタフェース)の記法がLambda式。
  • (arg) -> {body} 実装すべきメソッドが一つだけ:関数型インタフェース。

Runnable: void run()
Collable<T>: T call()
Comparetor<T>: boolean compare(T t1, T t2)

  • Javadocを見る癖をつけること。

ExecutorService service=...
service.submit(
//something to do async
);

・Function in Javascript: →function(){} 。Javaではできない。代わりに、匿名クラス。

service.submit(
() -> {..}
);

  • 処理を直接渡したいので関数を使う。
  • JavaSE8から、汎用的に使えるインタフェースがいっぱい入っている。

java.util.function
Customer, Function, Predicator, Supplier

  • 引数、戻り値だけはしっかりAPIを見る。
  • 数字の比較。x-yでも良いが、大きいとオーバフローを起こすので、Compareを使うこと。

Comparator<Integer> comp = new Comparator<Integer>(){
public Integer compare(Integer x, Integer y){
return int.compare(x,y);
}
};

  • ラムダ式で書くと、メソッド名も、インタフェースのnewも書かなくて良い。Compare<Integer> comp = (Integer x, Integer y) -> {return Integer. compare(x,y); };
  • Compare<Integer> comp= (x,y) -> {return Integer.compare(x,y); }; で型を抜いてもOK。
  • Compare<Integer> comp= (x,y) -> Integer.compare(x,y);
  • 一行の時にはreturnも{}もいらない。 Consumer<Integer> cons = n -> foo(n);
  • 引数がひとつの時にはここまでシンプルにできる。
  • NetBeansEclipseにはラムダ式に置き換える機能がある。

 

Streamの話

  • 内部イテレータを実現する。今までのイテレータは値が返せない。
  • Stream<T> for Object  IntStream, LongStream, DoubleStream for Primitive
  • JavaOneの話だと、Java9では、Stream<T>にStream<int>と書けるかもしれない。ただし普通にスルーされることもあるので実現するかどうか不明。

List<String> texts=..
Stream<String> st1 = texts.stream();
Stream<String> st2 = Arrays.stream(array);
BufferdReader reader=..
Stream<String> st3 = reader.lines();
Path path = ..
Stream<String> st4 = Files.lines(path);

  • これまでファイルをwhileで読んでいたのを全く書かずにFiles.linesで書けるようになっている。IOExceptionは出るのでtry&catchは必要。

IntStream st1 = IntStream.range(0,10);
DoubleStream st4 = DoubleStream.generate( () -> Math.ramdom() );

  • 戻り値をStreamの要素にする。

Stream<String> st3 = Stream.of("a", "b"); オブジェクトを作る。

List<String> texts = ..
texts
.stream()
.filter(s-> s.contains(" "))
.map(s -> s.toLowerCase())
...
.forEach(w -> System.out.println(w));

 

中間操作・終端操作

  • パイプライン。中間操作、終端操作に何があるかは覚える必要がある。5種類覚えるとたいてい書ける。

  • Intermidiate Ope. filter、map、flatMap
  • Terminal Ope. forEach、reduce、collect
  • s.filter(n->n%2==0); (2で割り切れるtrueなものだけfilterする)
  • s.map(n->n*2.0); (それぞれに2.0をかける。mapは地図ではなく写像。)s.filter(t->Arrays.stream(t.split(" ")); ("a b"、"c d"を"a"、"b"、"c"、"d"に。二重のループにしなくて良い。
  • s.reduce(0,(x,y)->x+y); (0と1234があった場合、0+1、1+2、3+3、6+4。次に繰り越す。Hadoopのreduce。足し算程度にしか使えない。)
collect

Stream<Integer> s1 = Stream.of(0,1,2,3);
List<Integer> l = s1.collect(Collectors.toList()); →[0,1,2,3]
Stream<String> s2 = Stream.of("all","big","asia");
Map<String List....
a all,asia 、b big
String joinText = s3.Collect(Collectors.joining();
int sum = s4.collect(Collectors.summingInt(s -> s.length()));

 

<例>
class Customer{
..
public String getName() {}
};
for (Customer c: csts){
if (c.getCityCode() == TOKYO){
tokyoCsts.add(c);
}
}

  • if文(elseなし)を見たらFilterと思え。
  • 新しいListを返しているのを見たらcollectと思え。

以下のように書き換えられる。
tokyoCsts = csts.stream()
.filter(c->c.getCityCode() ==TOKYO)
.collect(Collectors.toList());

 

streamをparallelStream()にするとパラレル処理になる。

for(Customer c: csts){
if(c.getCityCode() == TOKYO){
Stringname = c.getName();
tokyoCsts.add(name);
}
}


以下のように書き換えられる。
List<String> tkyCsts = csts.stream()
.filter(c -> c.getCityCode == TOKYO)
.map(c -> c.getName())
.collect

 

nullチェックはOPTIONALクラスで置き換えられる。
Customer c = getCustomer();
if(c != null){
doSomething(c);
}

 

以下のように、もし何か入っていれば処理をする、に書き換えられる。NullPointerExceptionで落ちなくなる。
Optional<Customer> opt = getCustomer();
opt.ifPresent(c -> doSometing(c));

 

もし要素があるならgetnameする。値がなければ""を返す。nameにnullが入ってこなくなる。NullPointerExceptionで落ちなくなる。
Optilnal<Customer> max = csts.stream()
.max*1. orElse("");
(まだJava標準ライブラリでもOptionalを使っているのは少ない。)

 

 


Lambda お稽古 (資料:https://github.com/skrb/LambdaOkeiko

 

*1:c1, c2) -> c1.getTrans() - c2.getTrans();
String name = max.map( c-> c.getName(