Java 8 (3) Stream 流 - 簡介

什麼是流?java

  流是Java API的新成員,它容許你以聲明性方式處理數據集合(經過查詢語言來表達,而不是臨時編寫一個實現)。就如今來講你能夠先把它當作是一個遍歷數據集的高級迭代器。此外,流還支持並行,你無需寫任何多線程的代碼!數據庫

定義一個實物類編程

public class Dish {
    //菜名
    private String Name;
    //是否素食
    private boolean vegetarian;
    //熱量
    private Integer Calories;
    //分類
    private Type type;
}

public enum Type {
    MEAT,//肉
    FISH,//魚
    OTHER//其餘
}

定義一個menu集合,如下栗子基於此menu數組

        List<Dish> menu = Arrays.asList(
                new Dish("豬肉燉粉條",false,800,Type.MEAT),
                new Dish("小炒牛肉",false,700,Type.MEAT),
                new Dish("宮保雞丁",false,400,Type.MEAT),
                new Dish("地三鮮",true,530,Type.OTHER),
                new Dish("水煮菠菜",true,350,Type.OTHER),
                new Dish("拔絲地瓜",true,120,Type.OTHER),
                new Dish("火山下雪",true,550,Type.OTHER),
                new Dish("水煮魚",false,330,Type.FISH),
                new Dish("因而乎",false,450,Type.FISH)
        );

舉個栗子:返回熱量低的菜餚名稱數據結構

//java7

        List<Dish> lowCaloricDishes = new ArrayList<>();
        for(Dish d : menu){
            if(d.getCalories() < 400){
                lowCaloricDishes.add(d);
            }
        }

        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
            @Override
            public int compare(Dish o1, Dish o2) {
                return Integer.compare(o1.getCalories(),o2.getCalories());
            }
        });

        List<String> lowCaloricDishesName = new ArrayList<>();
        for(Dish d : lowCaloricDishes){
            lowCaloricDishesName.add(d.getName());
        }
//java 8
        List<String> lowCalorDishesNames = menu.stream()
                .filter(s->s.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(toList());

經過使用stream()流來完成這個功能,代碼變的更簡潔了,使用stream你能夠把幾個基礎的操做鏈接起來,來表達複雜的數據處理流水線(在filter後面接上sorted、map和collect操做)。多線程

只要將.stream()更換爲parallelStream()就能夠完成並行操做,並行會在後面將。編程語言

 

流簡介ide

  Java 8中的集合支持了一個新的stream方法,返回一個流。後面你會看到還有不少其餘的方法能夠獲得流。一個簡短的定義就是「從支持數據處理操做的源生成的元素序列」。函數式編程

元素序列:就像集合同樣,流也提供了一個接口,能夠訪問特定元素類型的一組有序值。但流的目的在於表達計算,集合講的是數據。函數

源:流會使用一個提供數據的源,如集合、數組或輸入/輸出資源。從有序集合生成流時會保留原有的順序。

數據處理操做:流的數據處理功能支持相似於數據庫的操做,以及函數式編程語言中的經常使用操做(如filter、map、reduce、find、match、sort等),流能夠順序執行也能夠並行執行。

流還有兩個重要的特色:

  流水線:不少流操做自己會返回一個流,這樣多個操做就能夠鏈接起來,造成一個大的流水線。

  內部迭代:與使用迭代器顯示迭代集合不一樣,流的迭代操做是在背後進行的。

代碼解析:

  menu.stream() :從menu源得到流

  .filter(s->s.getCalories() < 400) : 過濾複合lambda表達式的數據

  .map(Dish::getName) : 獲取菜名

  .limit(3) : 只取前三個

  .collect(toList()); : 將結果保存在另外一個List中

結果:[拔絲地瓜, 水煮魚, 水煮菠菜]

 

流與集合

  集合是一個內存中的數據結構,它包含數據結構中目前全部的值-集合中的每一個元素都得先算出來才能添加到集合中,相似於存在DVD中的電影,從DVD上讀出全部信息,

  流是在概念上固定的數據結構(你不能添加或刪除 集合能夠),氣元素是按需計算的,流就像是一個延遲建立的集合:只有在消費者須要的時候纔會計算值,相似於在互聯網上看電影。

只遍歷一次 

  流和迭代器相似,只能遍歷一次。遍歷完以後,咱們就說這個流已經被消費掉了。因此能夠從原始數據源那裏再得到一個新的流從新遍歷一遍。

外部迭代與內部迭代

  使用Collection接口須要用戶去作迭代如foreach,這稱爲外部迭代。使用Streams庫則是內部迭代-它幫你把迭代作了,還把獲得的流值存在了某個地方,你只要給出一個函數便可。

        List<String> lowCaloricDishesName = new ArrayList<>();
        for (Dish d : lowCaloricDishes) {
            lowCaloricDishesName.add(d.getName());
        }
        //for-each 內部實際上是Iterator
        Iterator<Dish> iterator = menu.iterator();
        while(iterator.hasNext()){
            Dish d = iterator.next();
            lowCaloricDishesName.add(d.getName());
        }
//沒有迭代       
List<String> lowCalorDishesNames = menu.stream()
      .map(Dish::getName) //提取菜名
      .collect(toList());

內部迭代與外部迭代的區別:

  外部迭代一個集合時,顯示的獲取每一個項目再加以處理。

     (如:你:大兒子咱們如今把地上的玩具都收起來吧,還有玩具嗎?

            女兒:「有,球」,

    你:「好,把球放框裏吧,還有嗎?」

    女兒:「有,還有積木」

    你:「好,還有嗎?」

    女兒:「沒了,沒有了」

    你:「ok,收好了」)

  而內部迭代時,項目能夠透明地並行處理,或者用更優化的順序進行處理。

   (如:你只須要說:「大兒子把地上全部的玩具都放進盒子裏」就行了。 內部迭代的好處:第1、你大兒子能夠左手拿一個球,右手拿一個積木,第2、你大兒子能夠選擇先拿離盒子比較近的東西,而後再拿別的。)

 

流操做

  java.util.stream.Stream中的stream藉口定義了許多操做。他們能夠分爲兩大類:中間操做和終端操做。

  中間操做:

    如filter或sorted等中間操做會返回另外一個流,這讓多個操做能夠鏈接起來造成一個查詢,除非流水線上觸發一個終端操做,不然中間操做不會執行任何處理。

  終端操做:

    如count,foreach終端操做會從流的流水線生成結果,其結果是任何不適流的值,好比List、Integer,甚至void。例如,下面的流水線中forEach是一個返回void的終端操做。

menu.stream().forEach(System.out::println);

獲取全部熱量高於300的菜去掉重複值的數量

long count = menu.stream().filter(s->s.getCalories()>300).distinct().count();

 

使用流

  使用流通常包括三件事:一個數據源(執行查詢)、一箇中間操做連(造成一條流水線)、一個終端操做(執行流水線,並生成結果)。

 

到目前爲止見過的流的 中間操做有:

  filter:用於過濾集合 Lambda使用的函數式接口是Predicate<T>  函數描述符是T -> boolean

  map:用於映射到一個新的類型 函數式接口是Function<T,R> 函數描述符是 T -> R

  limit: 用於截斷流 獲取前幾個 

  sorted:用於排序流 函數式接口是Comparator<T> 函數描述符是 (T,T) -> int

  distinct: 用戶去重

終端操做有:

  forEach: 消費流中的每一個元素並對其應用lambda表達式

  count: 返回流中元素的個數,long

  collect: 把流規約成一個集合,好比List、Map、Integer等

 

小結:

  1.流是從支持數據處理操做的源生成的一系列元素

  2.流利用內部迭代:迭代經過filter、map、sorted等操做被抽象掉了

  3.流操做有兩類:中間操做和終端操做

  4.filter和map等中間操做會返回一個流,並能夠鏈接一塊兒,能夠用他們來設置一條流水線,但不會生成任何結果

  5.forEach和count等終端操做會返回一個非流的值,並處理流水線以返回結果

  6.流中的元素是按需計算的

相關文章
相關標籤/搜索