兩個星期以前,就有讀者強烈要求我寫一篇 Java Stream 流的文章,我說市面上不是已經有很多了嗎,結果你猜他怎么說:“就想看你寫的啊!”你看你看,多么蒼白的喜歡啊,那就“勉為其難”寫一篇吧,嘻嘻,

單從“Stream”這個單詞上來看,它似乎和 java.io 包下的 InputStream 和 OutputStream 有些關系,實際上呢,沒毛關系,Java 8 新增的 Stream 是為了解放程式員操作集合(Collection)時的生產力,之所以能解放,很大一部分原因可以歸功于同時出現的 Lambda 運算式——極大的提高了編程效率和程式可讀性,
Stream 究竟是什么呢?
Stream 就好像一個高級的迭代器,但只能遍歷一次,就好像一江春水向東流;在流的程序中,對流中的元素執行一些操作,比如“過濾掉長度大于 10 的字串”、“獲取每個字串的首字母”等,
要想操作流,首先需要有一個資料源,可以是陣列或者集合,每次操作都會回傳一個新的流物件,方便進行鏈式操作,但原有的流物件會保持不變,
流的操作可以分為兩種型別:
1)中間操作,可以有多個,每次回傳一個新的流,可進行鏈式操作,
2)終端操作,只能有一個,每次執行完,這個流也就用光光了,無法執行下一個操作,因此只能放在最后,
來舉個例子,
List<String> list = new ArrayList<>();
list.add("武漢加油");
list.add("中國加油");
list.add("世界加油");
list.add("世界加油");
long count = list.stream().distinct().count();
System.out.println(count);
distinct() 方法是一個中間操作(去重),它會回傳一個新的流(沒有共同元素),
Stream<T> distinct();
count() 方法是一個終端操作,回傳流中的元素個數,
long count();
中間操作不會立即執行,只有等到終端操作的時候,流才開始真正地遍歷,用于映射、過濾等,通俗點說,就是一次遍歷執行多個操作,性能就大大提高了,
理論部分就扯這么多,下面直接進入實戰部分,
01、創建流
如果是陣列的話,可以使用 Arrays.stream() 或者 Stream.of() 創建流;如果是集合的話,可以直接使用 stream() 方法創建流,因為該方法已經添加到 Collection 介面中,
public class CreateStreamDemo {
public static void main(String[] args) {
String[] arr = new String[]{
"武漢加油", "中國加油", "世界加油"};
Stream<String> stream = Arrays.stream(arr);
stream = Stream.of("武漢加油", "中國加油", "世界加油");
List<String> list = new ArrayList<>();
list.add("武漢加油");
list.add("中國加油");
list.add("世界加油");
stream = list.stream();
}
}
查看 Stream 原始碼的話,你會發現 of() 方法內部其實呼叫了 Arrays.stream() 方法,
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
另外,集合還可以呼叫 parallelStream() 方法創建并發流,默認使用的是 ForkJoinPool.commonPool()執行緒池,
List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();
02、操作流
Stream 類提供了很多有用的操作流的方法,我來挑一些常用的給你介紹一下,
1)過濾
通過 filter() 方法可以從流中篩選出我們想要的元素,
public class FilterStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰倫");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<String> stream = list.stream().filter(element -> element.contains("王"));
stream.forEach(System.out::println);
}
}
filter() 方法接收的是一個 Predicate(Java 8 新增的一個函式式介面,接受一個輸入引數回傳一個布林值結果)型別的引數,因此,我們可以直接將一個 Lambda 運算式傳遞給該方法,比如說 element -> element.contains("王") 就是篩選出帶有“王”的字串,
forEach() 方法接收的是一個 Consumer(Java 8 新增的一個函式式介面,接受一個輸入引數并且無回傳的操作)型別的引數,類名 :: 方法名是 Java 8 引入的新語法,System.out 回傳 PrintStream 類,println 方法你應該知道是列印的,
stream.forEach(System.out::println); 相當于在 for 回圈中列印,類似于下面的代碼:
for (String s : strs) {
System.out.println(s);
}
很明顯,一行代碼看起來更簡潔一些,來看一下程式的輸出結果:
王力宏
2)映射
如果想通過某種操作把一個流中的元素轉化成新的流中的元素,可以使用 map() 方法,
public class MapStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰倫");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<Integer> stream = list.stream().map(String::length);
stream.forEach(System.out::println);
}
}
map() 方法接收的是一個 Function(Java 8 新增的一個函式式介面,接受一個輸入引數 T,回傳一個結果 R)型別的引數,此時引數 為 String 類的 length 方法,也就是把 Stream<String> 的流轉成一個 Stream<Integer> 的流,
程式輸出的結果如下所示:
3
3
2
3
3)匹配
Stream 類提供了三個方法可供進行元素匹配,它們分別是:
anyMatch(),只要有一個元素匹配傳入的條件,就回傳 true,allMatch(),只有有一個元素不匹配傳入的條件,就回傳 false;如果全部匹配,則回傳 true,noneMatch(),只要有一個元素匹配傳入的條件,就回傳 false;如果全部匹配,則回傳 true,
public class MatchStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰倫");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("王"));
boolean allMatchFlag = list.stream().allMatch(element -> element.length() > 1);
boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉"));
System.out.println(anyMatchFlag);
System.out.println(allMatchFlag);
System.out.println(noneMatchFlag);
}
}
因為“王力宏”以“王”字開頭,所以 anyMatchFlag 應該為 true;因為“周杰倫”、“王力宏”、“陶喆”、“林俊杰”的字串長度都大于 1,所以 allMatchFlag 為 true;因為 4 個字串結尾都不是“沉”,所以 noneMatchFlag 為 true,
程式輸出的結果如下所示:
true
true
true
4)組合
reduce() 方法的主要作用是把 Stream 中的元素組合起來,它有兩種用法:
Optional<T> reduce(BinaryOperator<T> accumulator)
沒有起始值,只有一個引數,就是運算規則,此時回傳 [Optional][],
T reduce(T identity, BinaryOperator<T> accumulator)
有起始值,有運算規則,兩個引數,此時回傳的型別和起始值型別一致,
來看下面這個例子,
public class ReduceStreamDemo {
public static void main(String[] args) {
Integer[] ints = {
0, 1, 2, 3};
List<Integer> list = Arrays.asList(ints);
Optional<Integer> optional = list.stream().reduce((a, b) -> a + b);
Optional<Integer> optional1 = list.stream().reduce(Integer::sum);
System.out.println(optional.orElse(0));
System.out.println(optional1.orElse(0));
int reduce = list.stream().reduce(6, (a, b) -> a + b);
System.out.println(reduce);
int reduce1 = list.stream().reduce(6, Integer::sum);
System.out.println(reduce1);
}
}
運算規則可以是 [Lambda 運算式][Lambda](比如 (a, b) -> a + b),也可以是類名::方法名(比如 Integer::sum),
程式運行的結果如下所示:
6
6
12
12
0、1、2、3 在沒有起始值相加的時候結果為 6;有起始值 6 的時候結果為 12,
03、轉換流
既然可以把集合或者陣列轉成流,那么也應該有對應的方法,將流轉換回去——collect() 方法就滿足了這種需求,
public class CollectStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰倫");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
String[] strArray = list.stream().toArray(String[]::new);
System.out.println(Arrays.toString(strArray));
List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
System.out.println(list1);
System.out.println(list2);
String str = list.stream().collect(Collectors.joining(", ")).toString();
System.out.println(str);
}
}
toArray() 方法可以將流轉換成陣列,你可能比較好奇的是 String[]::new,它是什么東東呢?來看一下 toArray() 方法的原始碼,
<A> A[] toArray(IntFunction<A[]> generator);
也就是說 String[]::new 是一個 IntFunction,一個可以產生所需的新陣列的函式,可以通過反編譯位元組碼看看它到底是什么:
String[] strArray = (String[])list.stream().toArray((x$0) -> {
return new String[x$0];
});
System.out.println(Arrays.toString(strArray));
也就是相當于回傳了一個指定長度的字串陣列,
當我們需要把一個集合按照某種規則轉成另外一個集合的時候,就可以配套使用 map() 方法和 collect() 方法,
List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
通過 stream() 方法創建集合的流后,再通過 map(String:length) 將其映射為字串長度的一個新流,最后通過 collect() 方法將其轉換成新的集合,
Collectors 是一個收集器的工具類,內置了一系列收集器實作,比如說 toList() 方法將元素收集到一個新的 java.util.List 中;比如說 toCollection() 方法將元素收集到一個新的 java.util.ArrayList 中;比如說 joining() 方法將元素收集到一個可以用分隔符指定的字串中,
來看一下程式的輸出結果:
[周杰倫, 王力宏, 陶喆, 林俊杰]
[3, 3, 2, 3]
[周杰倫, 王力宏, 陶喆, 林俊杰]
周杰倫, 王力宏, 陶喆, 林俊杰

原文鏈接
關注公眾號:java寶典
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/265027.html
標籤:Java
上一篇:死磕Spring之IoC篇 - Bean 的創建程序
下一篇:Java面向物件

