流初接觸緩存
定義Dish類,它有兩個屬性,一個是名稱name,另外一個是卡路里calories,而且有針對這兩個屬性的get和set方法,以下:app
; { ; ; () { ; } (calories) { .= calories; } () { ; } (name) { .= name; } }
接下來定義5個Dish對象,並把這些Dish對象放到集合menu中,以下:ide
<> = ArrayList<>(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add();
遍歷menu集合,找出低卡路里的Dish對象,即找出卡路里小於400的Dish對象,但這些低卡路里對象須要放到另外容器中,以下:spa
<> = ArrayList<>(); (: ){ (.getCalories() < ){ .add(); } }
即又定義了一個名稱叫lowCaloricDishes的List做爲裝低卡路里對象容器。
code
此時,咱們還想讓lowCaloricDishes容器中的Dish對象按卡路里從低向高排序,那麼按Java8以前的實現,一般會用匿名類進行排序:視頻
.(, <>() { (o1, o2) { o1.getCalories() - o2.getCalories(); } });
最後還有點意猶未盡,想把低卡路里且按從低到高順序排查後的Dish對象名稱放到另外一個容器中對象
<> = ArrayList<>(); (: ){ .add(.getName()); }
在上面這點代碼中就不斷地使用中間容器List來建立緩存,當前Java8以前,你們都是這樣寫的,若是按Java8該如何修改呢?blog
List<String> lowCaloricDishesName = menu.stream().filter(d -> d.getCalories() < 400).sorted(Comparator.comparing(Dish::getCalories)).map(Dish::getName).collect(Collectors.toList());
其中filter(d -> d.getCalories() < 400)篩選出低卡路里的菜排序
sorted(comparing(Dish::getCalories))按卡路里從低向高排序get
map(Dish::getName)獲取菜的名稱
collect(toList())將名稱保存在List中
書中將Java8這段代碼叫聲明性方式:只須要說明想要作啥,而不用說明如何實現。同時它把filter、sorted、map和collect稱之爲流水線,在上面的示例中,這幾個流操做鏈是串行的。多盯下圖幾遍,瞭解一下流的概念:
2. 流與集合
集合能夠理解爲數據容器,裏面裝的都是數據; 流是計算,是對數據的計算。書中有一個例子,好比咱們在網上看視頻,一個靜態視頻有許多幀(把幀當作數據),則這個視頻就能夠理解爲集合,由於它裝着不少幀數據; 當咱們在網上點擊播放時,它並非把完整的視頻下載下來後再播放(那是很早以前的年代那會作的事兒),而是一邊播放一邊下載,正在播放的這段緩存就是流。
這個問題比較好理解,就再也不囉嗦。
<> = .(, , ); .stream().forEach(.::println); .stream().forEach(.::println);
特別注意,下面兩行是徹底同樣的,先把集合轉換成流,而後再遍歷打印,它是正常的。
<> = .(, , ); <> = .stream(); .forEach(.::println); .forEach(.::println);
但這段代碼就會拋出異常,由於流只能遍歷一次,換言之,在遍歷過程當中它被消費掉了。
這兩個代碼片斷依舊以在線看電影爲例來理解:
第一種是兩次點播; 第二種是在一次點播中針對某段流嘗試着兩次放映。
3. 外部迭代和內部迭代
<> = ArrayList<>(); (: ){ (.getCalories() < ){ .add(); } }
這種就叫外部迭代,即咱們人爲地使用代碼進行遍歷集合。固然,這段代碼使用迭代器遍歷,也是外部迭代,以下:
<> = .listIterator(); (.hasNext()){ = .next(); (.getCalories() < ){ .add(); } }
書上稱之爲醜陋的代碼?啊,多麼痛的打臉呀。
接下來,你告訴Lambda去遍歷,而不用你本身去遍歷,就叫內部迭代,以下:
menu.stream().filter(d -> d.getCalories() < 400);
你只須要去讓Lambda去把低卡路里的Dish對象挑出來便可,至於它是怎麼作的,你不用關心。
4. 流操做、中間操做和終端操做
能夠把filter、map、limit和collect分別稱爲鏈,而這些鏈構成一個流,因此整個流水線就是一個流操做,而把對最後一個的操做稱爲End操做,中間的操做稱爲中間操做。這裏看一下它們的類型:
menu List<String>
menu.stream(). stream<String>流 開始操做
filter(d -> d.getCalories() > 300). stream<String>流 中間操做
map(Dish::getName). stream<String>流 中間操做
limit(3). stream<String>流 中間操做
collect(toList()); List<string> 終端操做
爲了瞭解中間到底經歷了什麼,咱們打印一下代碼:
.stream().filter(d -> { ..println(+ d.getName()); d.getCalories() > ; }).map(d -> { ..println(+ d.getName()); d.getName(); }).limit().collect(.());
打印結果:
filtering d1 # 按條件過濾第1個元素,不符合直接進入下一個
filtering d2 #按條件過濾第2個元素,符合就獲取名稱:map name
mapping d2
filtering d3. #按條件過濾第3個元素,符合就獲取名稱:map name
mapping d3
filtering d4. #按條件過濾第4個元素,符合就獲取名稱:map name
mapping d4
終端操做:
終端操做會從流的流水線生成結果,其結果是任何不是流的值,好比List,Integer,甚至是void,但它不是stream