一、什么是stream?
Stream是使用一種類似用SQL語句從數(shù)據(jù)庫查詢數(shù)據(jù)的直觀方式來提供一種對Java集合運算和表達的高階抽象
Stream(流)是一個來自數(shù)據(jù)源的元素隊列并支持聚合操作
- 元素是特定類型的對象,形成一個隊列。Java中的Stream并不會存儲元素,而是按需計算
- 數(shù)據(jù)源流的來源。可以實集合,數(shù)組,I/Ochannel,生產(chǎn)器generator等
- 聚合操作類似SQL語句一樣的操作,比如filter,map,reduce,find,match,sorted等
和以前的Collection操作不同,Stream操作還有兩個基礎(chǔ)的特征:
- Pipelining:中間操作都會返回流對象本身。這樣多個操作可以串聯(lián)成一個管道,如同流式風(fēng)格(fluent style)。這樣做可以對操作進行優(yōu)化,比如延遲執(zhí)行(laziness)和短路(short-circuiting)
- 內(nèi)部迭代:以前對集合遍歷都是通過lterator或者For-Each的方式,顯式的在集合外部進行迭代,這叫外部迭代。Stream提供了內(nèi)部迭代的方式,通過訪問者模式實現(xiàn)。
二、Stream的特點
無存儲:Stream不是一種數(shù)據(jù)結(jié)構(gòu),它只是某種數(shù)據(jù)源的一個視圖,數(shù)據(jù)源可以是一個數(shù)組,Java容器或I/O channel等
為函數(shù)式編程而生:對Stream的任何修改都不會修改背后的數(shù)據(jù)源,比如對Stream執(zhí)行過濾操作并不會刪除被過濾的元素,而是會產(chǎn)生一個不包含被過濾元素的新Stream
惰式執(zhí)行:Stream上的操作并不會立即執(zhí)行,只有等到用戶真正需要結(jié)果的時候才會執(zhí)行
可消費性:Stream只能被"消費"一次,一旦遍歷就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成
圖上可見:獲取一些帶有顏色的球作為數(shù)據(jù)源,首先過濾掉紅色的、把它們重構(gòu)成隨機的三角形。再過濾掉小的三角形,最后計算出剩余圖形的周長
對于流的處理,主要有三種關(guān)鍵性操作:分別是流的創(chuàng)建操作、中間操作intermediate operation)以及最終操作(terminal operation)
三、創(chuàng)建操作
通過已有的集合來創(chuàng)建流(常見)
在Java 8中,除了增加了很多Stream相關(guān)的類以外,還對集合類自身做了增強,在其中增加了stream方法,可以將一個集合類轉(zhuǎn)換為流
List<String> strings = Arrays.asList("stream", "stream1", "stream2", "stream3", "stream4");
Stream<String> stream = strings.stream();
以上,通過一個已有的List創(chuàng)建一個流。除此以外,還有一個parallelStream方法,可以為集合創(chuàng)建一個并行流(多線程方式,需要考慮線程安全問題)
通過Stream創(chuàng)建流
可以使用Stream類提供的方法,直接返回一個由指定元素組成的流。
- of方法
Stream<String> stream = Stream.of("stream", "stream1", "stream2", "stream3", "stream4");
如以上代碼,直接通過of方式,創(chuàng)建并返回一個Stream
- generator方法(很少用)
四、Stream中間操作
Stream有很多重點操作,多個中間操作可以連接起來形成一個流水線,每一個中間操作就像流水線上的一個工人,每個工人都可以對流水進行加工,加工后得到結(jié)果還是一個流
常用中間操作列表
1.filter
filter方法用于通過設(shè)置的條件過濾出元素(用于過濾成一個新的流)。
public static void main(String[] args) {
List<String> strings = Arrays.asList("stream1", "", "stream2");
strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);
}
2.concat
concat方法將兩個Stream連接在一起,合成一個Stream。若兩個輸入的Stream都是排序,則新Stream也是排序的;若輸入的Stream中任何一個是并行,則新的Stream也是并行;若關(guān)閉新的Stream時,原來兩個輸入的Stream都將執(zhí)行關(guān)閉處理
.forEach(integer -> System.out.print(integer + " "));
// 1 2 3 4 5
3.map
map方法用于映射每個元素到對應(yīng)的結(jié)果,如下例子:使用map輸出了元素對應(yīng)的平方數(shù)
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().map( i -> i*i).forEach(System.out::println);
//9,4,4,9,49,9,25
它的意思是傳入一個類型,返回一個類型,就是你本來是String的流,可以轉(zhuǎn)換成Student這個類的流,或者其他的形式的流。
4.flatMap
flatMap方法于map方法類似,都是將原Stream中的每一個元素通過函數(shù)轉(zhuǎn)換,不同的是,該函數(shù)轉(zhuǎn)換的對對象是一個Stream,也不會在創(chuàng)建一個新的Stream,而是將原Stream的元素取代為轉(zhuǎn)換的Stream。如果轉(zhuǎn)換的Stream。如果轉(zhuǎn)換函數(shù)生產(chǎn)的Stream為null,應(yīng)由空Stream取代。flatMap有三個對于原始類型的變種方法,分別是:flatMapToInt,flatMapToLong和flatMapToDouble
Stream.of(1, 2, 3)
.flatMap(integer -> Stream.of(integer * 10))
.forEach(System.out::println);
// 10,20,30
5.peek
peek方法生產(chǎn)一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數(shù)(consumer實例),新Stream每個元素被消費的時候都會執(zhí)行給定的消費函數(shù),并且消費函數(shù)優(yōu)先執(zhí)行,如果不是很清楚,可以看看consumer函數(shù)就知道了
Stream.of(1, 2, 3, 4, 5)
.peek(integer -> System.out.println("accept:" + integer))
.forEach(System.out::println);
// accept:1
// 1
// accept:2
// 2
// accept:3
// 3
// accept:4
// 4
// accept:5
// 5
6.limit/skip
limit返回Stream的前面n個元素;skip則是跳過前n個元素。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().limit(4).forEach(System.out::println);
//3,2,2,3
numbers.stream().skip(4).forEach(System.out::println);
//7,3,5
7.distinct
distinct主要用來去重
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);
//3,2,7,5
五、Stream最終操作
Stream的中間操作得到的結(jié)果還是一個Stream,那么如何把一個Stream轉(zhuǎn)換成我們需要的類型?比如計算出流中元素的個數(shù)、將流轉(zhuǎn)換成集合等。這就是需要最終操作
最終操作會消耗流,產(chǎn)生一個最終結(jié)果。也就是說,在最終操作之后,不能再次使用流,也不能使用任何一個中間操作,否則將拋出異常
常見的最終操作
1.forEach
Stream提供了方法'forEach'來迭代流中的每個數(shù)據(jù)
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
2.count
count用來統(tǒng)計流中的元素個數(shù)
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
System.out.println(numbers.stream().count());
3.collect
collect就是一個歸約操作,可以接受各種做法作為參數(shù),將流中的元素累積成一個匯總結(jié)果
List<String> strings = Arrays.asList("Stream", "Stream大神", "Stream菜雞");
List<Integer> collect = strings.stream().filter(string -> string.length() <= 6).map(String::length).sorted().limit(2).distinct().collect(Collectors.toList());



