《Java8 實戰(zhàn)》- 讀書筆記第一章(02)
2018-12-29 22:56 folder Java label Java8
從方法傳遞到 Lambda
接著上次的 Predicate讹剔,繼續(xù)來了解一下挎春,如果繼續(xù)簡化代碼江掩。
把方法作為值來傳遞雖然很有用甩卓,但是要是有很多類似與 isHeavyApple 和 isGreenApple 這種可能只用一兩次的方法定義一堆確實有點(diǎn)煩人。為了解決這個問題蹄梢,Java8 它引入了一套新記法(匿名函數(shù)或 Lambda)恃锉,然你可以這樣寫:
List<Apple> isRedApples = filterApples(FilteringApples.apples, apple -> "red".equals(apple.getColor()));
或者是:
List<Apple> appleList = filterApples(FilteringApples.apples, apple -> apple.getWeight() < 120 && "red".equals(apple.getColor()));
甚至隔盛,你都可以不需要使用 filterApples 這個方法了,直接使用 Stream 中的 filter 方法就可以解決了:
List<Apple> isGreenApple = apples.stream().filter(apple -> "green".equals(apple.getColor())) .collect(Collectors.toList());
酷朗儒,看起來很不錯颊乘。所以,你甚至都不需要為只用一次的方法寫定義醉锄;這樣的代碼看起來更簡潔乏悄、更清晰,因為你用不著去找自己到底傳遞了什么代碼恳不。
流
在剛剛篩選蘋果的過程中檩小,就有使用到 Stream(流)其中的一個方法,這個 Stream 和 InputStream烟勋、OutputStream 是兩個完全不同的東西规求。Stream 它是 Java8 中的一個核心新特,它是一套新的用來處理集合的 API卵惦,有很多類似與 filter 這樣的方法而且使用起來非常的簡單和簡潔阻肿,可以簡化大部分代碼并且在并行的情況下利用多核 CPU,能很有效的提升對集合處理的性能鸵荠。
本章只是簡單的介紹了一下流的使用方式冕茅,至于流的詳細(xì)用法后面的章節(jié)會提到的。
現(xiàn)在蛹找,有一串字符串姨伤,需要進(jìn)行篩選并且轉(zhuǎn)為大寫以進(jìn)行排序,在 Java8 之前是我們是這么干的:
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開頭的字符串轉(zhuǎn)為大寫庸疾,添加到集合 cList.add(s.toUpperCase()); }}// 排序Collections.sort(cList);// 遍歷打印for (String s : cList) { System.out.println(s);}
這樣的代碼看起來很頭疼乍楚,需要寫這么長一段的代碼,在 Java8 中可以使用 Stream 進(jìn)行優(yōu)化:
List<String> stringList = Arrays.asList("a1", "a2", "b1", "c1", "c2", "c4", "c3");stringList.stream() // 篩選出以c開頭的字符串 .filter(s -> s.startsWith("c")) // 將剛剛以c開頭的字符串轉(zhuǎn)為大寫 .map(String::toUpperCase) // 排序 .sorted() // 循環(huán)遍歷 .forEach(System.out::println);
太棒了届慈,只需要短短的一行代碼就可以完成徒溪!但是,使用 Stream 它也是有缺點(diǎn)的金顿,它的性能不如 foreach 的效率高為了解決這個問題臊泌,Stream 支持并。使用并行能極大的利用多核 CPU 的優(yōu)勢揍拆,例如說:這些代碼原本只是用單核進(jìn)行處理渠概,現(xiàn)在有一臺 8 核的 CPU 電腦,那么它的處理速度就會是單核的八倍。
我們來進(jìn)行比較一下播揪,生成一個 0-100 的數(shù)字并寫入到文件中贮喧,循序流 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));
執(zhí)行結(jié)果(I5-6200U 的筆記本上執(zhí)行結(jié)果):
循序流:10251并行流:2620
效率明顯要比循序流快很多嘛!但是猪狈,并行流并不是萬能的箱沦,如果把 sleep 去掉后并且數(shù)字加到 100 萬,你會發(fā)現(xiàn)運(yùn)行的時間比循序流還要長雇庙。
去掉 sleep 并且生成的數(shù)字是 0-100 萬谓形,所消耗的時間:
循序流:2775并行流:3346
至于為什么有時候并行流效率比循序流還低,這個以后的文章會解釋疆前。
默認(rèn)方法
默認(rèn)方法是 Java8 中的一個新特性套耕,它的出現(xiàn)使得接口的升級變得平滑了,因為子類不是必須再去顯示的實現(xiàn)接口中的方法了峡继。
例如:在 Java8 中冯袍,你可以直接調(diào)用 List 接口中的 sort 方法、它是用 Java8 List 接口中如下所示的默認(rèn)方法實現(xiàn)的:
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 的任何實體類都不需要顯示的實現(xiàn) sort碾牌,而在以前的 Java 版本中康愤,除非提供了 sort 的實現(xiàn),否則這些實體類都無法編譯通過舶吗。但是征冷,默認(rèn)方法也存在著一些問題,一個類可以實現(xiàn)多個接口誓琼,那么好幾個接口多有同樣的默認(rèn)方法检激,那么這是否意味著 Java 中有了某種形式的多繼承?如果是多繼承腹侣,那么會不會出現(xiàn)像 C++ 中菱形繼承的問題叔收?這些問題以后的文章中都會有解釋和解決方案。
第一章總結(jié):
- 了解了 Java8 中的一些核心新特性傲隶,例如:Lambda 表達(dá)式饺律、Stream、默認(rèn)方法跺株。
- 了解了 Lambda 表達(dá)式和 Stream 為代碼帶來的簡潔性复濒。
- 并行流帶來的好處。
- Java8 中的默認(rèn)方法帶來的好處乒省。
代碼案例:Github-https://github.com/NGLSL/Java8InAction-ReadingNotes.git