Java 8 流的學習總結

1、首先談談什麼是流?

流是從支持數據處理操作的源中生成的元素序列。

拆分理解這句話:

  • 元素序列:流提供了一個接口,可以訪問特定元素類型的一組有序值;
  • :被處理的數據,從有序集合生成流時會保留原有的順序;由列表生成的流,其元素順序和列表一致;
  • 數據處理操作:與數據庫操作類似,流操作可以順序執行,也可以並行執行;
  • 流水線:很多流操作本身會返回一個流,這樣多個操作就可以鏈接起來,形成一條大的流水線;
  • 內部迭代:與使用迭代器顯示迭代的集合不同,流的迭代操作是在背後進行的;

流有什麼好處?或者我們爲什麼要使用流?

        // 取出用戶中大於24歲的人,並排序後輸出
        List<User> list = Arrays.asList(
                new User("Dz", 20),
                new User("Ym", 18),
                new User("Json", 28),
                new User("Dion", 25),
                new User("Tom", 32),
                new User("Kitty", 27)
        );

        // 傳統的 Java 編碼 start:

        List<User> newList = new ArrayList<>();

        for (User aList : list) {
            if (aList.getAge() > 24) {
                newList.add(aList);
            }
        }

        Collections.sort(newList, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        });

        for (User item : newList) {
            System.out.println(item.getName() + ":" + item.getAge());
        }

        // 傳統的 Java 編碼 end

        // 流操作
        list.stream()   //list.parallelStream() 可以併發執行
                .filter(u -> u.getAge() > 24)
                .sorted(Comparator.comparing(User::getAge))
                .forEach(user -> System.out.println(user.getName() + ":" +user.getAge()));

流允許你以使用聲明性的方式處理數據集合,而不是臨時編寫一個實現。具體好處如下(結合上述示例):

  • 聲明性 - 更簡潔、易讀;
  • 可複合 - 更靈活;
  • 可並行 - 性能更好;

 

2、流與集合對比

  • 集合是一個內存中的數據結構,包含數據結構中目前所有的值;
  • 而流是概念上固定的數據結構,其元素是按需加載的;
  • 流只能被遍歷一次,遍歷完之後就被消費掉了;
  • 遍歷數據的方式也是他們一個關鍵區別:
    • Collection 需要用戶去編碼迭代,這稱爲外部迭代;
    • Streams 庫使用內部迭代,不需要去編碼,它內部幫忙把這些事情做了;
  • 集合主要是訪問數據,而流的目的在於數據計算

 

3、流操作

流主要兩種類別的操作:中間操作終端操作

中間操作形成流水線、而終端操作則是從流水線中生成結果。

使用流則包含三個要素:

  • 數據源;
  • 中間操作鏈;
  • 終端操作;

 

4、使用流

4.1 篩選: (.filter 篩選比較用的比較多)

  • filter():該操作接受一個謂詞,篩選出符合謂詞的元素的流;
  • distinct():返回一個元素各異的流(根據流所生成元素的 hashCode 和 equals 方法實現);
  • limit(n):截短流,返回一個不會超過給定長度的流,若流是有序的,最多返回前 n 個原色;
  • skip(n):跳過元素,返回一個丟棄掉前 n 個元素的流,如果流中不足 n 個則返回一個空流;

4.2 映射: (map 將一個實體映射成一個新的元素,如獲取一個實體的一項屬性)

  • map():對流中每個一個元素應用函數,並將其映射成一個新的元素;
List<String> words = Arrays.asList("Java", "Lambda", "Work", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
  • flatMap(Arrays::stream):流的扁平化,把所有的流連接 起來成爲一個流;
// 列 words 出裏面各不相同的字符
// .map(word -> word.split("")) 返回的是 Stream<String[]>,不是我們想要的Stream<String>

String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);

words.stream()
.map(word -> word.split(""))
.map(Arrays::stream)
.distinct()
.collect(toList());


List<String> uniqueCharacters =
words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());

// 所有使用 map(Arrays::stream) 時生成的單個流都被合併起來,即扁平化爲一個流

4.3 查找與匹配: (查找滿足條件的一項)

Stream API 通過 allMatch、anyMatch、noneMatch、findFirst 和 findAny 方法提供了這樣的工具。

Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny()
.ifPresent(d -> System.out.println(d.getName());    

// 介紹如下文所示

Optional簡介

Optional<T>類(java.util.Optional)是一個容器類,代表一個值存在或不存在。

  • isPresent()將在 Optional 包含值的時候返回 true, 否則返回 false;
  • ifPresent(Consumer<T> block)會在值存在的時候執行給定的代碼塊;
  • T get()會在值存在時返回值,否則拋出一個 NoSuchElement 異常;
  • T orElse(T other)會在值存在時返回值,否則返回一個默認值;

 

4.4 歸約:(將流歸約成一個值 .reduce())

  • 對流求和;
  • 流中最大值、最小值問題;
  • 求流中元素個數;

.reduce()

  • 一個初始值,這裏是0;
  • 一個 BinaryOperator<T>來將兩個元素結合起來產生一個新值,這裏我們用的是 lambda (a, b) -> a + b;

無初始值:reduce 還有一個重載的變體,它不接受初始值,但是會返回一個 Optional 對象;

Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
//爲什麼它返回一個Optional<Integer>呢?考慮流中沒有任何元素的情況。
//reduce操作無法返回其和,因爲它沒有初始值。


//當然你也可以利用 Optional 自帶的幾個方法:
int sum = numbers.stream().reduce((a, b) -> (a + b)).orElse(0);

 

5 數值流 -- 原始類型流特化

  • IntStream;
  • DoubleStream;
  • LongStream;

 

6、總結

  • Streams API 可以表達複雜的數據處理查詢;
  • 你可以使用 filter、distinct、skip 和 limit 對流做篩選和切片;
  • 你可以使用 map 和 flatMap 提取或轉換流中的元素;
  • 你可以使用 findFirst 和 findAny 方法查找流中的元素。你可以用 allMatch 、noneMatch 和 anyMatch 方法讓流匹配給定的謂詞;
  • 這些方法都利用了短路:找到結果就立即停止計算;沒有必要處理整個流;
  • 你可以利用 reduce 方法將流中所有的元素迭代合併成一個結果,例如求和或查找最大、最小元素;
  • filter 和 map 等操作是無狀態的,它們並不存儲任何狀態。reduce 等操作要存儲狀態才能計算出一個值。sorted 和 distinct 等操作也要存儲狀態,因爲它們需要把流中的所
  • 有元素緩存起來才能返回一個新的流。這種操作稱爲有狀態操作;
  • 流有三種基本的原始類型特化:IntStream、DoubleStream 和 LongStream。它們的操作也有相應的特化;
  • 流不僅可以從集合創建,也可從值、數組、文件以及 iterate 與 generate 等特定方法 創建;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章