天天看點

Java8之stream流流是什麼流的定義流的内部優化流的使用

流是什麼

流是Java8 API的新功能,它允許使用聲明性方式處理集合。可以将其看成周遊資料集的進階疊代器。此外,流還可以透明地并行處理。

例如下面這行代碼:

// 利用多核架構并行處理
    menus.parallelStream()
        // 選出400卡路裡以下的菜
        .filter(dish -> dish.getCalories() < 400)
        // 按照卡路裡排序
        .sorted(Comparator.comparing(Dish::getCalories))
        // 提取菜名
        .map(Dish::getName)
        // 隻選擇頭三個
        .limit(3L)
        // 将結果儲存在List集合裡
        .collect(Collectors.toList())
        // 列印結果
        .forEach(System.out::println);           

流的優點:

  1. 代碼是以聲明性方式寫的:說明想要完成什麼而非說明如何實作該操作。
  2. 可以将多個基礎操作連結起來,以此來表達複雜的資料處理流水線,同時保持代碼清晰可讀。
menus=>start: menus
List=>end: List
filter=>subroutine: filter
sorted=>subroutine: sorted
map=>subroutine: map

menus(right)->filter(right)->sorted(right)->map(right)->List

           

流的定義

簡單來說,流就是從支援資料處理操作的源生成的元素序列。
  • 元素序列:就像集合一樣,流提供一個接口,通過該接口可以通路特定元素類型的一組有序值。因為集合是資料結構,是以它的主要目的是以特定的時間/空間複雜度存儲和通路元素。但流的目的在于表達計算。
  • 源:流會使用一個提供資料的源,這個源可以是集合、數組或輸入資源。若從有序集合生成流時會保留原有的順序。
  • 資料處理操作:流的資料處理功能支援類似資料庫的操作,以及函數式程式設計語言中的常用操作,如

    filter

    map

    reduce

    find

    match

    sort

    等。流操作可以順序執行,也可以并行執行。

流的特點

  1. 流水線——很多流操作本身會傳回一個流,這樣多個操作就可以連結起來,形成一個大的流水線。流水線的操作可以看作對資料源進行資料庫式的查詢。
  2. 内部疊代——與使用疊代器顯示疊代的集合不同,流的疊代操作是在背後進行的。

流的内部優化

我們看一段代碼:

menu.stream()
//        篩選出不是蔬菜的食物
        .filter(dish -> {
          System.out.println(dish.toString());
          System.out.println("------------this is filter--------------");
          return !dish.getVegetarian();
        })
//        将對象轉換為string類型
        .map(dish->{
          System.out.println(dish.toString());
          System.out.println("------------this is map--------------");
          return dish.toString();
        })
//        隻擷取四個
        .limit(4L)
        .collect(Collectors.toSet())
        .forEach(System.out::println);           

列印在控制台的結果讓人驚歎——

Dish(name=pork, vegetarian=false, calories=800, type=MEAT)
------------this is filter--------------
Dish(name=pork, vegetarian=false, calories=800, type=MEAT)
------------this is map--------------
Dish(name=beef, vegetarian=false, calories=700, type=MEAT)
------------this is filter--------------
Dish(name=beef, vegetarian=false, calories=700, type=MEAT)
------------this is map--------------
Dish(name=chick, vegetarian=false, calories=400, type=MEAT)
------------this is filter--------------
Dish(name=chick, vegetarian=false, calories=400, type=MEAT)
------------this is map--------------
Dish(name=french fries, vegetarian=true, calories=530, type=OTHER)
------------this is filter--------------
Dish(name=rice, vegetarian=true, calories=350, type=OTHER)
------------this is filter--------------
Dish(name=season fruit, vegetarian=true, calories=120, type=OTHER)
------------this is filter--------------
Dish(name=pizza, vegetarian=true, calories=550, type=OTHER)
------------this is filter--------------
Dish(name=prawns, vegetarian=false, calories=300, type=FISH)
------------this is filter--------------
Dish(name=prawns, vegetarian=false, calories=300, type=FISH)
------------this is map--------------
Dish(name=chick, vegetarian=false, calories=400, type=MEAT)
Dish(name=prawns, vegetarian=false, calories=300, type=FISH)
Dish(name=beef, vegetarian=false, calories=700, type=MEAT)
Dish(name=pork, vegetarian=false, calories=800, type=MEAT)           
這些優化用到了流的延遲性質。盡管

filter

map

是兩個獨立的操作,但是它們合并到同一次周遊中。

流的使用

  • filter

    方法:該方法會接受一個

    boolean

    類型結果的函數,并傳回一個符合條件的流。

例如:

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        篩選出偶數
        .filter(num -> num % 2 == 0)
        .forEach(System.out::println);

// result: 2, 4, 6, 6, 4, 2           
  • distinct

    方法:該方法會去除流中重複的元素(根據流所生成元素的

    hashCode

    equals

    方法實作)。
Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        去除重複的元素
        .distinct()
        .forEach(System.out::println);

// result: 1, 2, 3, 4, 5, 6, 7           
  • limit

    方法:該方法會傳回一個不超過給定長度的流。
Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        擷取前5個對象
        .limit(5L)
        .forEach(System.out::println);

// result: 1, 2, 3, 4, 5           
  • skip

    方法:該方法會傳回一個扔掉了前n個元素的流。
Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        扔掉前5個對象
        .skip(5L)
        .forEach(System.out::println);

// result: 6, 7, 6, 5, 4, 3, 2, 1           
  • map

    方法:該方法會對每個元素進行函數運算,并将其映射為一個新的元素(傳回函數運算結果)。

例如(對每個元素進行函數運算):

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
//        對每個元素進行統一的操作
        .map(num -> ++num)
        .forEach(System.out::println);

// result: 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2           

例如(對集合内某一進制素進行提取:

List<Dish> menu = Arrays.asList(
        new Dish("pork", false, 800, Type.MEAT),
        new Dish("beef", false, 700, Type.MEAT),
        new Dish("chick", false, 400, Type.MEAT),
        new Dish("french fries", true, 530, Type.OTHER),
        new Dish("rice", true, 350, Type.OTHER),
        new Dish("season fruit", true, 120, Type.OTHER)
    );
//    提取每道菜的名字
    List<String> dishNames = menu.stream()
        .map(Dish::getName)
        .collect(Collectors.toList());
    dishNames.forEach(System.out::println);

//result: pork, beef, chick, french fries, rice, season fruit           
  • flatMap

    方法:該方法會将一個流中的每個元素轉換為流,一個元素對應一個流,然後把所有的流再連接配接起來成為一個流。
List<List<String>> lists = Arrays.asList(
        Arrays.asList("1", "2", "3"),
        Arrays.asList("4", "5", "6"),
        Arrays.asList("7", "8", "9")
    );
    List<String> strings = lists.stream()
//        将每個集合都轉換為數組
        .map(List::toArray)
//        将每個數組内的元素轉換為流
        .flatMap(Arrays::stream)
        .map(Object::toString)
        .collect(Collectors.toList());
    strings.forEach(System.out::println);

// result: "1", "2", "3", "4", "5", "6", "7", "8", "9"           
  • anyMatch

    方法:該方法會判斷流中是否有至少一個元素能比對給定的判斷條件。
//    判斷菜單裡是否有蔬菜
    boolean isVegetarian = menu.stream().anyMatch(Dish::getVegetarian);           
  • allMatch

    方法:該方法會判斷流中是否任何元素都能比對給定的判斷條件。
//    判斷菜單裡是否全部都是蔬菜
    boolean isVegetarian = menu.stream().allMatch(Dish::getVegetarian);           
  • noneMatch

    方法:該方法會判斷流中是否任何元素都不比對給定的判斷條件。
//    判斷菜單裡是否沒有蔬菜
    boolean isVegetarian = menu.stream().noneMatch(Dish::getVegetarian);           
  • findAny

    方法:該方法會傳回流中的任意元素。
Optional<Dish> optionalDish = menu.stream()
        .filter(Dish::getVegetarian)
        .findAny();
//    是否存在蔬菜類食物
    boolean isVegetarian = optionalDish.isPresent();
    if (isVegetarian){
//    如果存在則輸出該食物資訊
      System.out.println(optionalDish.get().toString());
      return;
    }
    System.out.println("not vegetarian in menu");
  }           
  • findFirst

    方法:該方法會傳回流中的地一個元素。
Optional<Integer> optionalInteger = Stream.of(1, 2, 3, 4, 5, 6)
        .filter(num -> num % 3 == 0)
        .findFirst();
    boolean isHave = optionalInteger.isPresent();
    if (isHave) {
      System.out.println(optionalInteger.get().toString());
      return;
    }
    System.out.println("not num");
  }           
  • reduce

    方法:該方法有接收辨別符符合

    BinaryOperator<T>

    的表達式,将兩個元素結合起來傳回一個新的值。
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//    求和
    Integer sum = integerStream.reduce(Integer::sum).get();
//    最大值
    Integer max = integerStream.reduce(Integer::max).get();
//    最小值
    Integer min = integerStream.reduce(Integer::min).get();
    ......