本文是「Java 8 函數式編程」第三章的讀書筆記,章名爲流。本章主要介紹了外部迭代與內部迭代以及經常使用的高階函數。java
過去咱們要對一個List
進行迭代時,每每會採用以下方式:編程
int count = 0; for (Artist artist : artists) { if (artist.isFrom("London")) { count++; } }
而這種方法的原理,實際上是先調用iterator
方法,而後再迭代,等效於以下代碼:數組
int count = 0; Iterator<Artist> iterator = artists.iterator(); while (iterator.hasNext()) { Artist artist = iterator.next(); if (artist.isFrom("London")) { count++; } }
這樣的迭代方式,把迭代的控制權交給了iterator
對象,讓其控制整個迭代過程,這就叫作外部迭代
。數據結構
外部迭代須要咱們本身編寫迭代的控制代碼,顯得十分繁瑣。特別是對於Map
對象,繁瑣到我都不想給出例子。框架
外部迭代將行爲和方法混爲一談,難以對代碼進行重構操做。函數式編程
與之相對的就是內部迭代了。內部迭代就是把迭代的控制權交給了集合自己,讓集合本身實現相應的迭代,而調用者並不須要關心如何迭代。函數
要使用內部迭代,須要使用Java 8
中新增的接口Stream
。而集合框架都已經包含了一個stream()
方法,用於得到Stream
對象。oop
long count = artists.stream() .filter(artist -> artist.isFrom("London")) .count();
這個例子就是使用的內部迭代。先獲取stream
對象,而後調用filter
方法過濾,最後統計符合條件的個數。優化
在Java
中調用一個方法,一般會當即執行操做。然而Stream
裏的一些方法卻不太同樣,它們返回的對象不是新的集合,而是建立新集合的配方。咱們經過一個例子說明:spa
Stream<String> names = Stream.of("Bryant", "Jordon", "James") .filter(name -> { System.out.println(name); return name.length() == 6; }); System.out.println("counting"); System.out.println(names.count());
最終會獲得以下輸出:
counting Bryant Jordon James 2
出現這樣的結果,緣由是
像filter
這樣的方法,只會描述Stream
,最終不會產生新集合的方法叫作惰性求值方法
像count
這樣會從Stream
中產生值或集合等結果的方法叫作及早求值方法
判斷一個操做是惰性求值仍是及早求值,只須要看它的返回值
若是返回值是Stream
,則是惰性求值
返回的是一個值或null
,則是及早求值
在對集合使用流操做時,使用惰性求值方法造成一個惰性求值的鏈,最後用及早求值方法獲得結果,而集合只須要迭代一次。
collect:及早求值,經常使用於生成List
Map
或其餘複雜的數據結構
map:惰性求值,將一種類型的數據轉換成另外一種類型,將一個流中的值轉化成一個新的流,相似於Hadoop
裏的map
filter:惰性求值,過濾不符合條件的元素
flatMap:惰性求值,相似於map
,只是Function
參數的返回值限定爲Stream
,用於鏈接多個Stream
成爲一個Stream
max & min:及早求值,reduce
方法的特例,返回Optional
(第四章介紹)對象
reduce:及早求值,從一組值中生成一個值,相似於Hadoop
中的reduce
高階函數接收一個函數做爲參數,或者返回一個函數的函數。
Lambda
表達式明確要達成什麼轉化,而不是說明如何轉化
沒有反作用:
只經過函數的返回值就能充分理解函數的所有做用
函數不會修改程序或外界的狀態
獲取值而不是變量(避免使用數組逃過JVM
的追殺,應該考慮優化邏輯)