接着上次的Predicate,繼續來了解一下,若是繼續簡化代碼。java
把方法做爲值來傳遞雖然頗有用,可是要是有不少相似與isHeavyApple和isGreenApple這種可能只用一兩次的方法定義一堆確實有點煩人。爲了解決這個問題,Java8它引入了一套新記法(匿名函數或Lambda),然你能夠這樣寫:git
List<Apple> isRedApples = filterApples(FilteringApples.apples, apple -> "red".equals(apple.getColor()));
或者是:github
List<Apple> appleList = filterApples(FilteringApples.apples, apple -> apple.getWeight() < 120 && "red".equals(apple.getColor()));
甚至,你均可以不須要使用filterApples這個方法了,直接使用Stream中的filter方法就能夠解決了:app
List<Apple> isGreenApple = apples.stream().filter(apple -> "green".equals(apple.getColor())) .collect(Collectors.toList());
酷,看起來很不錯。因此,你甚至都不須要爲只用一次的方法寫定義;這樣的代碼看起來更簡潔、更清晰,由於你用不着去找本身到底傳遞了什麼代碼。函數
在剛剛篩選蘋果的過程當中,就有使用到Stream(流)其中的一個方法,這個Stream和InputStream、OutputStream是兩個徹底不一樣的東西。Stream它是Java8中的一個核心新特,它是一套新的用來處理集合的API,有不少相似與filter這樣的方法並且使用起來很是的簡單和簡潔,能夠簡化大部分代碼而且在並行的狀況下利用多核CPU,能頗有效的提高對集合處理的性能。性能
本章只是簡單的介紹了一下流的使用方式,至於流的詳細用法後面的章節會提到的。優化
如今,有一串字符串,須要進行篩選而且轉爲大寫以進行排序,在Java8以前是咱們是這麼幹的:this
List<String> stringList = Arrays.asList("a1", "a2", "b1", "c1", "c2", "c4", "c3"); List<String> cList = new ArrayList<>(); for (String s : stringList) { // 篩選出以c開頭的字符串 if (s.startsWith("c")) { // 將以c開頭的字符串轉爲大寫,添加到集合 cList.add(s.toUpperCase()); } } // 排序 Collections.sort(cList); // 遍歷打印 for (String s : cList) { System.out.println(s); }
這樣的代碼看起來很頭疼,須要寫這麼長一段的代碼,在Java8中能夠使用Stream進行優化:code
List<String> stringList = Arrays.asList("a1", "a2", "b1", "c1", "c2", "c4", "c3"); stringList.stream() // 篩選出以c開頭的字符串 .filter(s -> s.startsWith("c")) // 將剛剛以c開頭的字符串轉爲大寫 .map(String::toUpperCase) // 排序 .sorted() // 循環遍歷 .forEach(System.out::println);
太棒了,只須要短短的一行代碼就能夠完成!可是,使用Stream它也是有缺點的,它的性能不如foreach的效率高爲了解決這個問題,Stream支持並。使用並行能極大的利用多核CPU的優點,例如說:這些代碼本來只是用單核進行處理,如今有一臺8核的CPU電腦,那麼它的處理速度就會是單核的八倍。排序
咱們來進行比較一下,生成一個0-100的數字並寫入到文件中,循序流VS並行流誰的效率更高.
循序流:
long startTime = System.currentTimeMillis(); OutputStream out = new FileOutputStream(new File("D:/integer1.txt")); IntStream.rangeClosed(0, 100) .forEach(i -> { try { Thread.sleep(100L); out.write(i); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); long endTime = System.currentTimeMillis(); System.out.println("循序流:" + (endTime - startTime));
並行流:
long startTime = System.currentTimeMillis(); OutputStream out = new FileOutputStream(new File("D:/integer2.txt")); IntStream.rangeClosed(0, 100) .parallel().forEach(i -> { try { Thread.sleep(100L); out.write(i); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); long endTime = System.currentTimeMillis(); System.out.println("並行流:" + (endTime - startTime));
執行結果(I5-6200U的筆記本上執行結果):
循序流:10251 並行流:2620
效率明顯要比循序流快不少嘛!可是,並行流並非萬能的,若是把sleep去掉後而且數字加到100萬,你會發現運行的時間比循序流還要長。
去掉sleep而且生成的數字是0-100萬,所消耗的時間:
循序流:2775 並行流:3346
至於爲何有時候並行流效率比循序流還低,這個之後的文章會解釋。
默認方法是Java8中的一個新特性,它的出現使得接口的升級變得平滑了,由於子類不是必須再去顯式的實現接口中的方法了。
例如:在Java8中,你能夠直接調用List接口中的sort方法、它是用Java8 List接口中以下所示的默認方法實現的:
default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
這意味着List的任何實體類都不須要顯式的實現sort,而在之前的Java版本中,除非提供了sort的實現,不然這些實體類都沒法編譯經過。可是,默認方法也存在着一些問題,一個類能夠實現多個接口,那麼好幾個接口多有一樣的默認方法,那麼這是否意味着Java中有了某種形式的多繼承?若是是多繼承,那麼會不會出現像C++中菱形繼承的問題?這些問題之後的文章中都會有解釋和解決方案。
第一章總結:
代碼案例: