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);
- 引数がひとつの時にはここまでシンプルにできる。
- NetBeans、Eclipseにはラムダ式に置き換える機能がある。
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(