1、基本概念 html
λ表達式能夠被當作是一個Object。λ表達式的類型,叫作「目標類型(target type)」。λ表達式的目標類型是「函數接口(functional interface)」,這是Java8新引入的概念。它的定義是:一個接口,若是隻有一個顯式聲明的抽象方法,那麼它就是一個函數接口。通常用@FunctionalInterface標註出來(也能夠不標)。
java
@FunctionalInterface public interface Runnable { void run(); } public interface Callable<V> { V call() throws Exception; } public interface ActionListener { void actionPerformed(ActionEvent e); } public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }注意最後這個Comparator接口。它裏面聲明瞭兩個方法,貌似不符合函數接口的定義,但它的確是函數接口。這是由於equals方法是Object的,全部的接口都會聲明Object的public方法——雖然大可能是隱式的。因此,Comparator顯式的聲明瞭equals不影響它依然是個函數接口
集合類的批處理操做API的目的是實現集合類的「內部迭代」,並指望充分利用現代多核CPU進行並行計算。
Java8以前集合類的迭代(Iteration)都是外部的,即客戶代碼,不能充分利用cpu的多核資源。而內部迭代意味着改由Java類庫來進行迭代,而不是客戶代碼. express
Java8爲集合類引入了一個重要概念:流(stream)。一個流一般以一個集合類實例爲其數據源,而後在其上定義各類操做。流的API設計使用了管道(pipelines)模式。對流的一次操做會返回另外一個流。如同IO的API或者StringBuffer的append方法那樣,從而多個不一樣的操做能夠在一個語句裏串起來. 併發
2、λ表達式的使用 oracle
一個λ表達式只有在轉型成一個函數接口後才能被當作Object使用 app
能夠用一個λ表達式爲一個函數接口賦值,而後再賦值給一個Object ide
一個λ表達式能夠有多個目標類型(函數接口),只要函數匹配成功便可。但需注意一個λ表達式必須至少有一個目標類型
函數
λ表達式主要用於替換之前普遍使用的內部匿名類,各類回調,好比事件響應器、傳入Thread類的Runnable等
性能
new Thread( () -> { System.out.println("This is from an anonymous method (lambda exp)."); } );注意線程裏的λ表達式,你並不須要顯式地把它轉成一個Runnable,由於Java能根據上下文自動推斷出來:一個Thread的構造函數接受一個Runnable參數,而傳入的λ表達式正好符合其run()函數,因此Java編譯器推斷它爲Runnable。
filter方法的參數是Predicate類型,forEach方法的參數是Consumer類型,它們都是函數接口,因此可使用λ表達式
測試
3、代碼樣例
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.stream.Collectors; import java.util.stream.IntStream; public class Main { private static List<Person> persons = Arrays.asList(new Person("Joe", 12), new Person("Jim", 19), new Person("John", 21)); public static void main(String[] args) throws Exception { // testStreamAPI(); // testStreamMap(); // testStreamPerformance(); testInt(2, 3, 4, 2, 3, 5, 1); testOccurrence(2, 3, 4, 2, 3, 5, 1); distinctSum(2, 3, 4, 2, 3, 5, 1); testNestLambda(); } public static void testStreamAPI() { // 打印年齡大於12的人 System.out.println("使用順序流串行打印"); persons.stream().filter(p -> p.getAge() > 12) .collect(Collectors.toCollection(ArrayList::new)) .forEach(p -> { System.out.println(p); }); System.out.println("使用並行流並行打印,即利用多核技術可將大數據經過多核並行處理"); persons.parallelStream().filter(p -> p.getAge() > 12) .collect(Collectors.toCollection(ArrayList::new)) .forEach(p -> { System.out.println(p); }); } public static void testStreamMap() { // 應該用filter過濾,而後再使用map進行轉換 persons.parallelStream().map(p -> { if (p.getAge() > 18) return p; return null; }).collect(Collectors.toCollection(ArrayList::new)).forEach(p -> { if (p != null) System.out.println(p); }); persons.parallelStream().filter(p -> p.getAge() > 18) .map(p -> new Adult(p)) .collect(Collectors.toCollection(ArrayList::new)) .forEach(p -> { if (p != null) System.out.println(p); }); } public static void testStreamReduce() { persons.parallelStream().filter(p -> p.getAge() > 18) .map(p -> new Adult(p)) .collect(Collectors.toCollection(ArrayList::new)) .forEach(p -> { if (p != null) System.out.println(p); }); } public static void testStreamPerformance() { // 初始化一個範圍100萬整數流,求能被2整除的數字,toArray()是終點方法 long start1 = System.nanoTime(); int a[] = IntStream.range(0, 1_000_000).filter(p -> p % 2 == 0) .toArray(); System.out.printf("測試順序流的性能: %.2fs", (System.nanoTime() - start1) * 1e-9); long start2 = System.nanoTime(); int b[] = IntStream.range(0, 1_000_000).parallel() .filter(p -> p % 2 == 0).toArray(); System.out.printf(" 測試並行流的性能: %.2fs", (System.nanoTime() - start2) * 1e-9); // 本機的測試結果是:測試順序流的性能: 0.02s 測試並行流的性能: 0.01s // 在100萬時,併發流快些,1000萬,併發流反而會慢些,估計和線程的頻繁切換有關(本機是8線程CPU) } public static void testInt(Integer... numbers) { List<Integer> l = Arrays.asList(numbers); List<Integer> r = l.stream() .map(e -> new Integer(e)) .filter(e -> e > 2) .distinct() .collect(Collectors.toList()); System.out.println("testInt result is: " + r); } public static void testOccurrence(Integer... numbers) { List<Integer> l = Arrays.asList(numbers); Map<Integer, Integer> r = l .stream() .map(e -> new Integer(e)) .collect( Collectors.groupingBy(p -> p, Collectors.summingInt(p -> 1))); System.out.println("testOccurrence result is: " + r); } public static void distinctSum(Integer... numbers) { List<Integer> l = Arrays.asList(numbers); int sum = l.stream() .map(e -> new Integer(e)) .distinct() .reduce(0, (x, y) -> x + y); // equivalent to .sum() System.out.println("distinctSum result is: " + sum); } public static void testNestLambda() throws Exception{ Callable<Runnable> c1 = () -> () -> { System.out.println("Nested lambda"); }; c1.call().run(); // 用在條件表達式中 Callable<Integer> c2 = false ? (() -> 42) : (() -> 24); System.out.println(c2.call()); } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public String getName() { System.out.println(name); return name; } public int getAge() { return age; } @Override public String toString() { return "name:" + name + " age:" + age; } } class Adult extends Person { public Adult(Person p) { super(p.getName(), p.getAge()); } }
4、參考資料
http://openjdk.java.net/projects/lambda/
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html