java8學習:引入stream

期待l發表於2018-11-08

內容來自《 java8實戰 》,本篇文章內容均為非盈利,旨為方便自己查詢、總結備份、開源分享。如有侵權請告知,馬上刪除。
書籍購買地址:java8實戰

  • 這一篇內容要講的是流,關於流的概念,流的內部迭代跟外部迭代的區別之類的偏概念性的問題,下一篇在細講流到底是什麼用
  • 那麼說到流,我們自然就會想到水,這就涉及到水源頭是哪裡,水怎麼流過來的,水會被我們的桶收集起來,還是說用水杯收集起來,那麼問題就一下子出現了三個
  • 流的源頭是哪裡

    • 流的源頭包括集合,陣列或者一些輸入輸出資源,但是需要注意的是,從有序集合生成流時會保持原有的資料,從列表生成的流,其元素資料與列表一致
  • 流是怎麼個處理過程

    • 這其實就要設計到用到了具體的api了,下面會說到
  • 流的收集

    • 無非就是收整合集合等一些等儲存資料的資料結構中
  • 流和集合作對比

    • 一點很清楚的就是,流是來做計算的,而集合是用來儲存的,這個應該很容易理解
    • 還有一個就是:我們需要看一部電影,那麼無非就是兩種線上看和下載下來看,那麼線上看就屬於流模式,而整個資源下載下來看就屬於集合模式了,所以從簡單的說集合與流之間的差異就在與什麼時候計算。集合是一個記憶體中的資料結構,它包含了所有值,就比如我們使用的list,不管新增還是刪除他總是在記憶體中操作的,並且它是每個元素先計算出來再新增到集合的,而流呢?他就像我們線上看電影一樣,不需載入全部的資料,劇情無聊快進的時候,流的處理方式就是載入你點選的時間點之後的幾幀來供你觀看,這就是他們的區別
  • 好了扯了一大堆概念,讓我們來看看集合和流處理資料的方式

    • 我們利用下面這個類的結構來編寫程式碼
    @Data
    public class Dish {
        private final String name;
        private final boolean vegetarian;  //是否是素菜
        private final int calories;        //卡路里
        private final Type type;
        private enum Type{
            MEAT,FISH,OTHER;
        }
        public Dish(String name, boolean vegetarian, int calories, Type type) {
            this.name = name;
            this.vegetarian = vegetarian;
            this.calories = calories;
            this.type = type;
        }
    }
    • 好了讓我們建立一個集合
    List<Dish> menu = Arrays.asList(
            new Dish("apple",true,50, Dish.Type.OTHER),
            new Dish("chicken",false,350, Dish.Type.MEAT),
            new Dish("rich",true,150, Dish.Type.OTHER),
            new Dish("pizza",true,350, Dish.Type.OTHER),
            new Dish("fish",false,250, Dish.Type.FISH),
            new Dish("orange",true,70, Dish.Type.OTHER),
            new Dish("banana",true,60, Dish.Type.OTHER));
    • 集合操作:找出卡路里最低排名的前三位
    @Test
    public void test() throws Exception {
        menu.sort(new Comparator<Dish>() {
            @Override
            public int compare(Dish o1, Dish o2) {
                return o1.getCalories() - o2.getCalories();
            }
        });
        for (int i = 0; i < 3; i++) {
            System.out.println(menu.get(i));
        }
    }
    • 流操作:找出卡路里最低排名的前三位
    @Test
    public void test() throws Exception {
        List<Dish> collect = menu
                .stream()
                .sorted(Comparator.comparing(Dish::getCalories))
                .limit(3)
                .collect(Collectors.toList());
        for (Dish dish : collect) {
            System.out.println(dish);
        }
    }
    • 觀察上面兩種,肯定是流操作比較好一些,因為它操作的每一步都很清楚的告訴開發者他的流在幹什麼,如上就是流和集合的一個簡單對比
    • 如上其實也反映了兩個:內部迭代和外部迭代,流就屬於內部迭代,集合的操作的方法就是外部迭代

      • 內部迭代就是按照我們的意思,類似SQL似的查詢操作,比如取前三啊那就是limit(3),然後他會根據你的查詢條件自動的去遍歷你的集合,這樣就省去自己去一步步的寫邏輯了
      • 外部迭代自己理解的就是需要開發者手動編寫遍歷邏輯
    • 那麼流是一個一次性的,所以我們看如下
    Stream<Dish> limit = menu.stream()
            .sorted(Comparator.comparing(Dish::getCalories))
            .limit(3);
    List<Dish> collect1 = limit.collect(Collectors.toList());
    //IllegalStateException: stream has already been operated upon or closed
    List<Dish> collect2 = limit.collect(Collectors.toList());
    • 上面就充分說明了,流只能遍歷一次。當遍歷一次之後這個流就被消費掉了,如果重新需要,那麼需要從源頭那再獲取個stream
    • 我們看下面的程式碼
Stream<Dish> limit = menu.stream()
        .sorted(Comparator.comparing(Dish::getCalories))
        .limit(3);
System.out.println(limit);//java.util.stream.SliceOps$1@436e852b
List<Dish> collect1 = limit.collect(Collectors.toList()); //結果
  • 這段程式碼就可以說明了,流是一種類似懶載入模式的,只有觸發終端操作,那麼這個流才會開始啟動遍歷資料,如上我們一直到limit(3)這都是流的中間操作,流的中間操作是不會產生資料的,只有出發了collect方法這個終端方法才會開始流的遍歷工作,當然中間操作不止limit,sorted,終端操作也不止collect,這一篇只是簡單的引入一下流而已,下一篇內容將會講stream的使用

好了這一篇內容就介紹到這,主要介紹了流的概念,流和集合的區別,內外迭代和流的中間操作以及終端操作,還有重要的一點就是流是一次性的


相關文章