stream | 基礎(chǔ)知識

一、什么是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());

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容