Java8流

  1. 流初接觸緩存

定義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稱之爲流水線,在上面的示例中,這幾個流操做鏈是串行的。多盯下圖幾遍,瞭解一下流的概念:

image.png

image.png

image.png

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. 流操做、中間操做和終端操做

image.png

能夠把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

相關文章
相關標籤/搜索