學習Stream的目的 函數式編程漸漸變成主流,為了看懂同事的代碼。 相對於傳統的編程方式,代碼更為簡潔清晰易懂。 使得併發編程變得如此簡單。 有效的避免了代碼嵌套地獄。(見樣例) if (條件1) { if (條件2) { if (條件3) { // 再嵌套下去都快見到Diablo了。 } } } ...
學習Stream的目的
-
函數式編程漸漸變成主流,為了看懂同事的代碼。
-
相對於傳統的編程方式,代碼更為簡潔清晰易懂。
-
使得併發編程變得如此簡單。
-
有效的避免了代碼嵌套地獄。(見樣例)
if (條件1) { if (條件2) { if (條件3) { // 再嵌套下去都快見到Diablo了。 } } }
Stream的特點
- 不修改數據源:任何對於Stream對象的操作都不會修改數據源的數據。
- 延遲執行:中間操作只是對於一系列操作細節的定義,而非執行。只有在終端操作被調用的同時執行中間操作。
- 可消費:任何一個Stream對象執行了終端操作後都將不可再利用。只能操作由數據源生成的新的Stream。
Stream的分類
-
串列流:單項環境下的Stream,基礎。
List.of().stream();
-
並行流:多線程環境下的Stream,重點難點。本文中未涉及。
List.of().parallelStream();
Stream對象的創建
總共有三種方式:
-
經由集合對象創建
List<String> list = new ArrayList<>(); Stream<String> stream = list.stream();
-
經由數組對象創建(兩種)
String[] strs = new String[10]; // 將數組所有元素裝入stream Stream<String> stream1 = Arrays.stream(strs); // 將數組指定區間的元素裝入stream Stream<String> stream2 = Arrays.stream(strs, 1, 7);
-
使用Stream的靜態方法創建(三種)
// 由單個元素創建Stream,元素不允許為null Stream<String> stream1 = Stream.of("Test"); // 由單個元素創建stream,元素允許為null Stream<String> stream2 = Stream.ofNullable(null); // 由多個元素創建stream,內部其實調用的是Arrays.stream(T[] array) Stream<String> stream3 = Stream.of("Test1", "Test2", "Test3");
方法的分類
- 中間操作:根據調用的方法,返回各種各樣的stream對象。傳入的各種Lambda只是修改了該對象中對應方法的定義,而非執行。
- 終端操作:執行終端操作的方法,並且其間也執行中間操作對應的方法。
常用方法(入門)
中間操作
distinct
方法簽名:Stream<T> distinct()
作用:返回一個去重後的Stream。
List<String> list = List.of("1", "1");
list.stream().distinct()
.forEach(t -> System.out.println(t)); // 輸出:1
filter
方法簽名:Stream<T> filter(Predicate<? super T> predicate)
作用:返回一個由滿足predicate條件的元素構成的Stream。
List<Integer> list = List.of(1, 3, 5);
list.stream().filter(t -> t >= 3)
.forEach(t -> System.out.println(t)); // 輸出:3, 5
sorted
因為sorted存在兩種重載,並且在jdk源碼的實現並不相同,所以我們分開討論。
方法簽名:Stream<T> sorted()
作用:通過調用T類型重寫Comparable介面的compareTo方法排序,返回排序後的Stream。
// 此處省略了一些java文件定義的結構,請著眼於一下核心代碼
// NG例, 不實現Comparable介面
class MyInteger {
int value;
MyInteger(int value) {
this.value = value;
}
}
List<MyInteger> list = new ArrayList<>();
list.add(new MyInteger(4));
list.add(new MyInteger(1));
list.add(new MyInteger(3));
list.stream().sorted() // 不報錯
.forEach(t -> System.out.println(t.value)); // ClassCastException:不能被強轉成Comparable類型
// OK例, 實現Comparable介面(Integer實現了Comparable介面)
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().sorted()
.forEach(t -> System.out.println(t)); // 輸出:1 3 4
方法簽名:Stream<T> sorted(Comparator<? super T> comparator)
作用:通過調用傳入的comparator的compare方法排序,返回排序後的Stream。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().sorted((o1, o2) -> o2 - o1) // 傳入一個比較器的實現
.forEach(t -> System.out.println(t.value)); // 輸出:4 3 1
skip
方法簽名:Stream<T> skip(long n)
作用:返回一個不包含前n項的Stream。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().skip(2) // 跳過前兩個元素
.forEach(t -> System.out.println(t.value)); // 輸出:3
limit
方法簽名:Stream<T> limit(long n)
作用:返回一個包含前n項的Stream。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().limit(2) // 跳過前兩個元素
.forEach(t -> System.out.println(t.value)); // 輸出:4 1
peek
方法簽名:Stream<T> peek(Consumer<? super T> action)
作用:執行消費操作,返回原Stream。雖然有消費操作,但是Stream的狀態並不會改變。並不會真正消費Stream這一特點是的peek方法常用於調試。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Stream<Integer> stream = list.stream();
stream.peek(t -> System.out.println(t)); // 輸出:4 1 3, 並且不消費stream
stream.forEach(t -> System.out.println(t)); // 輸出:4 1 3, 並且消費stream(消費後stream不可再次使用)
stream.forEach(t -> System.out.println(t)); // IllegalStateException:stream已經被操作或關閉
map
方法簽名:Stream<T> map(Function<? super T, ? extends R> mapper)
作用:將原Stream中的所有元素類型從T轉化為R(不是強轉,是通過一些操作得到R類型),返回封裝R類型元素的Stream。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().map(t -> String.valueOf(t)) // 將元素都轉換成String
.forEach(t -> System.out.println(t)); // 輸出:4 1 3
flatMap
方法簽名:Stream<T> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
作用:將原Stream中的所有元素類型從T轉化為R,返回Stream<R>。與map的主要區別在於,適配一對多的數據類型。比如,類型T中存在一個List<String>,使用map返回一個Stream<List<String>>,而是用flatMap則返回一個Stream<String>,實現一對多的映射。
List<Integer[]> list = new ArrayList<>();
list.add(new Integer[] {1, 2, 3});
list.add(new Integer[] {4, 5, 3});
list.stream().flatMap(t -> Stream.of(t)) // 將各個數組中的元素都放入stream, 實現 1 → n 的轉換
.forEach(t -> System.out.println(t)); // 輸出:1 2 3 4 5 3
終端操作
forEach
方法簽名:void forEach(Consumer<? super T> action)
作用:迭代消費Stream里的所有元素,比如列印,寫入文件,寫入DB等。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
list.stream().forEach(t -> System.out.println(t)); // 輸出:4 1 3
toArray
方法簽名:Object[] toArray()
作用:將Stream中的所有元素封裝成Object[]返回。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Object[] objects = list.stream().toArray(); // 與元素類型無關, 固定返回Object[]
Integer[] ints = (Integer[])objects; // 使用時你可能需要類型強轉
count
方法簽名:long count()
作用:返回Stream中的元素個數。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
long count = list.stream().count(); // 返回stream中的元素個數, 當前為3
findFirst
方法簽名:Optional<T> findFirst()
作用:Stream的第一個元素封裝在Optional中返回。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional<Integer> first = list.stream().findFirst(); // 返回stream中的第一個元素, 當前為4
/*
Optional類內容比較多所以現在不做贅述, 大家姑且就認為是個只能存放一個元素的容器就好,以後會開一個新的博文詳細為 大家講解用法
*/
anyMatch
方法簽名:boolean anyMatch(Predicate<? super T> predicate)
作用:當存在符合predicate條件的元素時返回true,否則返回false。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
boolean bool = list.stream().anyMatch(t -> t <= 1); // 當一個元素小於等於1時就返回true, 當前true
allMatch
方法簽名:boolean allMatch(Predicate<? super T> predicate)
作用:當所有元素都符合predicate條件時返回true,否則返回false。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
boolean bool = list.stream().allMatch(t -> t <= 1); // 當所有元素小於等於1時才返回true, 當前false
noneMatch
方法簽名:boolean noneMatch(Predicate<? super T> predicate)
作用:當所有元素都不符合predicate條件時返回true,否則返回false。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
boolean bool = list.stream().noneMatch(t -> t <= 1); // 當所有元素都不小於等於1時才返回true, 當前false
min
方法簽名:Optional<T> min(Comparator<? super T> comparator)
作用:按照傳入的比較器將最小的元素封裝在Optional中返回。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional<Integer> min = list.stream().min((o1, o2) -> o2 - o1); // 根據傳入的比較器實現返回最小值, 當前4
/*
這個方法可以認為stream按照傳入的比較器排序, 返回排序後的第一個元素
*/
max
方法簽名:Optional<T> max(Comparator<? super T> comparator)
作用:按照傳入的比較器將最大的元素封裝在Optional中返回。
List<Integer> list = new ArrayList<>();
list.add(4);
list.add(1);
list.add(3);
Optional<Integer> max = list.stream().max((o1, o2) -> o2 - o1); // 根據傳入的比較器實現返回最大值
/*
這個方法可以認為stream按照傳入的比較器排序, 返回排序後的最後一個元素
*/
本文只討論了串列流的使用方法,對於並行流等進階的使用方法請參考 Java -> Stream進階(尚未更新)