兩個星期之前,就有讀者強烈要求我寫一篇 Java Stream 流的文章,我說市面上不是已經有不少了嗎,結果你猜他怎麼說:「就想看你寫的啊!」你看你看,多麼蒼白的喜歡啊。那就「勉爲其難」寫一篇吧,嘻嘻。java
單從「Stream」這個單詞上來看,它彷佛和 java.io 包下的 InputStream 和 OutputStream 有些關係。實際上呢,沒毛關係。Java 8 新增的 Stream 是爲了解放程序員操做集合(Collection)時的生產力,之因此能解放,很大一部分緣由能夠歸功於同時出現的 Lambda 表達式——極大的提升了編程效率和程序可讀性。git
Stream 到底是什麼呢?程序員
Stream 就好像一個高級的迭代器,但只能遍歷一次,就好像一江春水向東流;在流的過程當中,對流中的元素執行一些操做,好比「過濾掉長度大於 10 的字符串」、「獲取每一個字符串的首字母」等。github
要想操做流,首先須要有一個數據源,能夠是數組或者集合。每次操做都會返回一個新的流對象,方便進行鏈式操做,但原有的流對象會保持不變。面試
流的操做能夠分爲兩種類型:編程
1)中間操做,能夠有多個,每次返回一個新的流,可進行鏈式操做。數組
2)終端操做,只能有一個,每次執行完,這個流也就用光光了,沒法執行下一個操做,所以只能放在最後。bash
來舉個例子。微信
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();
複製代碼
中間操做不會當即執行,只有等到終端操做的時候,流纔開始真正地遍歷,用於映射、過濾等。通俗點說,就是一次遍歷執行多個操做,性能就大大提升了。
理論部分就扯這麼多,下面直接進入實戰部分。
若是是數組的話,可使用 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();
複製代碼
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 表達式(好比 (a, b) -> a + b
),也能夠是類名::方法名(好比 Integer::sum
)。
程序運行的結果以下所示:
6
6
12
12
複製代碼
0、一、二、3 在沒有起始值相加的時候結果爲 6;有起始值 6 的時候結果爲 12。
既然能夠把集合或者數組轉成流,那麼也應該有對應的方法,將流轉換回去——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]
[周杰倫, 王力宏, 陶喆, 林俊杰]
周杰倫, 王力宏, 陶喆, 林俊杰
複製代碼
好了,我親愛的讀者朋友,以上就是本文的所有內容了,是否是感受 Stream 流實在是太強大了?我是沉默王二,一枚有趣的程序員。原創不易,莫要白票,請你爲本文點贊個吧,這將是我寫做更多優質文章的最強動力。
若是以爲文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回覆【666】更有我爲你精心準備的 500G 高清教學視頻(已分門別類)。本文 GitHub 已經收錄,有大廠面試完整考點,歡迎 Star。