Java8集合中的Lambda表達式
譯文出處: 《四火的嘮叨》 原文出處:zeroturnaround
Lambdas表達式是Java 8的主題,在Java平臺上我們期待了很久。但是,如果如果我們不在集合中使用它的話,就損失了很大價值。把現有接口遷移成為lambda風格接口的問題已經通過default methods,也就是defender methods解決了。在這篇文章里面我們來看一看Java集合里面的批量數據操作(bulk operation)。
批量操作
最初的變更文檔已經說了,批量操作是“給Java集合框架添加的用以批量操作數據的功能,而它是基于lambda函數實現的”。引用的話也就是在說,lambda移植到Java 8對我來說的實際目的,就是它提供了一種新的使用集合的方式,這也是最重要的特性,表達式操作符可以并行執行,并且lambda是一個比常規表達式操作符更好的工具。
內部和外部的迭代
歷史上,Java集合是不能夠表達內部迭代的,而只提供了一種外部迭代的方式,也就是for或者while循環。要描述內部迭代,我們需要用到LambdaJ這樣的類庫:
List persons = asList(new Person("Joe"), new Person("Jim"), new Person("John")); forEach(persons).setLastName("Doe");
從上面的例子可以看出,我們不需要關心last name是怎么被設置到每一個person對象里面去的,也許這樣的行為是支持并發執行的。現在我們可以在Java 8中使用類似的表達了:
persons.forEach(p -> p.setLastName("Doe"))
內部迭代其實和集合的批量操作并沒有密切的聯系,這只是一個小小的特性,借助它我們感受到語法表達上的變化。真正有意思的和批量操作相關的是新的流(stream)API。
流API
新的java.util.stream包已經添加進JDK了,現在我們可以借助Java 8執行filter/map/reduce風格的操作了。
流API允許我們聲明對數據進行串行或者并行的操作:
List persons = … // sequential version Stream stream = persons.stream(); //parallel version Stream parallelStream = persons.parallelStream();
java.util.stream.Stream接口提供了批量數據操作的入口,取得了對流實例的引用,我們就可以對集合執行如下有趣的任務了:
Filter
在數據流中實現過濾功能是首先我們可以想到的最自然的操作了。Stream接口暴露了一個filter方法,它可以接受表示操作的Predicate實現來使用定義了過濾條件的lambda表達式。
List persons = … Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);
Map
假使我們現在過濾了一些數據,比如轉換對象的時候。Map操作允許我們執行一個Function的實現(Function<T,R>的泛型T,R分別表示執行輸入和執行結果),它接受入參并返回。首先,讓我們來看看怎樣以匿名內部類的方式來描述它:
Stream students = persons.stream() .filter(p -> p.getAge() > 18) .map(new Function() { @Override public Student apply(Person person) { return new Student(person); } });
現在,把上述例子轉換成使用lambda表達式的寫法:
Stream map = persons.stream() .filter(p -> p.getAge() > 18) .map(person -> new Student(person));
Lambda在把參數傳給map方法的時候,實際卻并沒有使用這個參數,那么我們就可以寫成這樣:
Stream map = persons.stream() .filter(p -> p.getAge() > 18) .map(Student::new);
Collect
“流”抽象天生就該是持續的,我們使用流來描述操作,但是如果我們要獲取最終結果的話,必須收集流產生的最終結果。Stream API提供了一系列“最終”的方法,collect()方法就是其中的一個,我們借此可以收集操作的最終結果:
List students = persons.stream() .filter(p -> p.getAge() > 18) .map(Student::new) .collect(new Collector>() { … });
幸運的是,大多數情況下你不需要自己實現Collector接口,而是利用Collectors工具類:
List students = persons.stream() .filter(p -> p.getAge() > 18) .map(Student::new) .collect(Collectors.toList());
或者,如果我們想使用特定的實現類來收集結果:
List students = persons.stream() .filter(p -> p.getAge() > 18) .map(Student::new) .collect(Collectors.toCollection(ArrayList::new));
并行和串行
一個使用新的Stream API有趣的特性是它從來都不需要所謂串行或者并行的方法,可以從一開始就并行地消費數據,或者在處理流中的任意時刻轉為串行的。
List students = persons.stream() .parallel() .filter(p -> p.getAge() > 18) // filtering will be performed concurrently .sequential() .map(Student::new) .collect(Collectors.toCollection(ArrayList::new));
這里有隱藏的一點是,數據處理的并行部分會自動地自我管理,不需要我們自己來處理并發的問題。
總結
好了,要結束了。新的Stream API和lambda表達式給Java 8帶來了很多新的特性。當然,在這篇文章以外還有很多沒有談及到,但愿很快我可以給你帶給你更多有趣的特性。