Java 8 Stream 使用

轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/106389071
本文出自【趙彥軍的博客】

1、基礎概念

Java 8 API添加了一個新的抽象稱爲流Stream,可以讓你以一種聲明的方式處理數據。

Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。

Stream API可以極大提高Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。

這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。

元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。

如果你用過 rxjava ,入門就很簡單。

2、常見操作符

filter 過濾

var list = mutableListOf("b", "a", "d", "c")

ist.stream()
    .filter {   
        it != "a"  //輸出元素不是 a 的元素
    }
   .forEach {
        Log.d("yanjun", it)
    }

輸出結果:

 D/yanjun: b
 D/yanjun: d
 D/yanjun: c

findFirst 查找第一個

var list = mutableListOf("b", "a", "d", "c")

var first = list.stream()
            .findFirst()  //查找第一個元素
            .get()
            
Log.d("yanjun", first)

輸出結果:

 D/yanjun: b

sorted 從小到大排序

var list2 = listOf(3, 1, 2)

list2.stream()
            .sorted()  //默認從小到大排序
            .forEach {
                Log.d("yanjun", it.toString())
            }

輸出結果:

 D/yanjun: 1
 D/yanjun: 2
 D/yanjun: 3

map 方法用於映射每個元素到對應的結果

var list = listOf(3, 1, 2)

list.stream()
     .map {
         it * 2   //所有元素都乘以 2 
     }
     .forEach {
          Log.d("yanjun", it.toString())
    }

輸出結果:

 D/yanjun: 6
 D/yanjun: 2
 D/yanjun: 4

limit 獲取指定數量的流

var list = listOf(3, 1, 2)

list.stream()
    .limit(2)  //只輸出前2個數據
    .forEach {
        Log.d("yanjun", it.toString())
    }

輸出結果:

 D/yanjun: 3
 D/yanjun: 1

collect 將流轉換成集合和聚合元素

var list = listOf(3, 1, 2)

var list2 = list.stream()
                .sorted()   //排序 結果爲:1 2 3 
                .collect(Collectors.toList())  //把流數據轉換成 list 

list2.forEach {
        Log.d("yanjun", it.toString())
     }                

輸出結果:

 D/yanjun: 1
 D/yanjun: 2
 D/yanjun: 3

average 求平均值

var list = listOf(3, 1, 2)

var value = list.average()  //求平均值
 
Log.d("yanjun", value.toString())               

輸出結果:

 D/yanjun: 2.0

3、Stream 執行順序

數據流的鏈式調用是垂直執行的, 但並不是每個操作符都是垂直的,sorted是水平執行的。
爲什麼要設計成這樣呢?原因是出於性能的考慮。

例子1

var list = mutableListOf("b", "a", "d", "c")

list.stream()
     .map {
         Log.d("yanjun map", it)
         it
     }
     .forEach {
         Log.d("yanjun forEach", it)
     }  

輸出結果:

D/yanjun map: b
D/yanjun forEach: b
D/yanjun map: a
D/yanjun forEach: a
D/yanjun map: d
D/yanjun forEach: d
D/yanjun map: c
D/yanjun forEach: c

例子2

var list = mutableListOf("b", "a", "d", "c")

list.stream()
     .map {
         Log.d("yanjun map", it)
         it
     }
     .sorted()   //排序
     .forEach {
         Log.d("yanjun forEach", it)
     }  

輸出結果:

D/yanjun map: b
D/yanjun map: a
D/yanjun map: d
D/yanjun map: c
D/yanjun forEach: a
D/yanjun forEach: b
D/yanjun forEach: c
D/yanjun forEach: d

4、數據流複用問題

Java8 Stream 流是不能被複用的,一旦你調用任何終端操作,流就會關閉:

var list = mutableListOf("b", "a", "d", "c")

var stream = list.stream()
        
//流第一次被使用,使用完畢後,流被關閉,不能重複使用
stream.forEach {
   Log.d("yanjun", it)
}

//流第二次被使用,會報異常
stream.forEach {
   Log.d("yanjun", it)
 }

報異常,程序出錯
在這裏插入圖片描述

5、並行流

集合支持parallelStream()方法來創建元素的並行流。或者你可以在已存在的數據流上調用中間方法parallel(),將串行流轉換爲並行流,這也是可以的。

var list = mutableListOf("b", "a", "d", "c")

list.parallelStream()   //創建並行流
     .map {
         Thread.sleep(5000)  //模擬耗時操作
         Log.d("yanjun map", Thread.currentThread().name + " " + it)
         it
     }
     .forEach {
         Log.d("yanjun forEach", Thread.currentThread().name + " " + it)
     }

輸出結果:

D/yanjun map: main d
D/yanjun forEach: main d
D/yanjun map: ForkJoinPool.commonPool-worker-1 a
D/yanjun map: ForkJoinPool.commonPool-worker-2 b
D/yanjun forEach: ForkJoinPool.commonPool-worker-1 a
D/yanjun forEach: ForkJoinPool.commonPool-worker-2 b
D/yanjun map: ForkJoinPool.commonPool-worker-3 c
D/yanjun forEach: ForkJoinPool.commonPool-worker-3 c

如您所見,並行流使用了所有的ForkJoinPool中的可用線程來執行流式操作。在持續的運行中,輸出結果可能有所不同,因爲所使用的特定線程是非特定的。

總結

  • 無存儲:Stream是基於數據源的對象,它本身不存儲數據元素,而是通過管道將數據源的元素傳遞給操作。
  • 函數式編程:對Stream的任何修改都不會修改背後的數據源,比如對Stream執行filter操作並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新的Stream。
  • 延遲執行:Stream的操作由零個或多箇中間操作(intermediate operation)和一個結束操作(terminal operation)兩部分組成。只有執行了結束操作,Stream定義的中間操作纔會依次執行,這就是Stream的延遲特性。
  • 可消費性:Stream只能被“消費”一次,一旦消費就會失效。就像容器的迭代器那樣,想要再次遍歷必須重新生成一個新的Stream。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章