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


流的定義

簡單來說,流就是從支持數據處理操作的源生成的元素序列。

  • 元素序列:就像集合一樣,流提供一個接口,通過該接口可以訪問特定元素類型的一組有序值。因爲集合是數據結構,所以它的主要目的是以特定的時間/空間複雜度存儲和訪問元素。但流的目的在於表達計算
  • 源:流會使用一個提供數據的源,這個源可以是集合、數組或輸入資源。若從有序集合生成流時會保留原有的順序。
  • 數據處理操作:流的數據處理功能支持類似數據庫的操作,以及函數式編程語言中的常用操作,如filtermapreducefindmatchsort等。流操作可以順序執行,也可以並行執行。

流的特點

  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)

這些優化用到了流的延遲性質。儘管filtermap是兩個獨立的操作,但是它們合併到同一次遍歷中。

流的使用

  • 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方法:該方法會去除流中重複的元素(根據流所生成元素的hashCodeequals方法實現)。

例如:

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();
    ......
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章