1.Stream簡介
1.Java 8 API添加了一個新的抽象稱爲流Stream,可以讓你以一種聲明的方式處理數據。
2.Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
3.Stream API可以極大提高Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。
4.這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
5.元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。
以上的流程轉換爲 Java 代碼爲:
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
2.Stream的優點
舉個例子: 有一個List列表,我們需要獲得年齡爲70歲的前10個Person的姓名。
過程式的解決方案:
List<Person> personList = fromDB(); // 獲得List<Person>
int limit = 10; // 限制條件
List<String> nameList = new ArrayList(); // 收集的姓名集合
for(Person personItem : personList){
if(personItem.age == 70){ // 滿足條件
nameList.add(personItem.name); // 加入姓名集合
if(nameList.size() >= 10){ // 判斷是否超過限制
break;
}
}
}
return nameList;
函數式stream解決方案:
List<Person> personList = fromDB(); // 獲得List<Person>
List<String> nameList = personList.stream()
.filter(item->item.age == 70) // 過濾條件
.limit(10) // limit限制條件
.map(item->item.name) // 獲得姓名
.collect(Collector.toList()); // 轉化爲list
.forEach(System.out::println);//遍歷並打印
return nameList;
兩種方案的不同之處:
1.從函數式的角度上看,過程式的代碼實現將收集元素、循環迭代、各種邏輯判斷耦合在一起,暴露了太多細節。當未來需求變動和變得更加複雜的情況下,過程式的代碼將變得難以理解和維護(需要控制檯打印出 年齡爲70歲的前10個Person中,姓王的Person的名稱)。
2.函數式的解決方案解開了代碼細節和業務邏輯的耦合,類似於sql語句,表達的是"要做什麼"而不是"如何去做",使程序員可以更加專注於業務邏輯,寫出易於理解和維護的代碼。
3.生成流和操作示例
在 Java 8 中, 集合接口有兩個方法來生成流:
-
stream() − 爲集合創建串行流。
-
parallelStream() − 爲集合創建並行流。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
forEach
Stream 提供了新的方法 ‘forEach’ 來迭代流中的每個數據。以下代碼片段使用 forEach 輸出了10個隨機數:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
map
map 方法用於映射每個元素到對應的結果,以下代碼片段使用 map 輸出了元素對應的平方數:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 獲取對應的平方數
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter
filter 方法用於通過設置的條件過濾出元素。以下代碼片段使用 filter 方法過濾出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 獲取空字符串的數量
long count = strings.stream().filter(string -> string.isEmpty()).count();
limit
limit 方法用於獲取指定數量的流。 以下代碼片段使用 limit 方法打印出 10 條數據:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
sorted
sorted 方法用於對流進行排序。以下代碼片段使用 sorted 方法對輸出的 10 個隨機數進行排序:
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
Collectors
Collectors 類實現了很多歸約操作,例如將流轉換成集合和聚合元素。Collectors 可用於返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("篩選列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合併字符串: " + mergedString);
統計
另外,一些產生統計結果的收集器也非常有用。它們主要用於int、double、long等基本類型上,它們可以用來產生類似如下的統計結果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的數 : " + stats.getMax());
System.out.println("列表中最小的數 : " + stats.getMin());
System.out.println("所有數之和 : " + stats.getSum());
System.out.println("平均數 : " + stats.getAverage());
並行(parallel)程序
parallelStream 是流並行處理程序的代替方法。以下實例我們使用 parallelStream 來輸出空字符串的數量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 獲取空字符串的數量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
我們可以很容易的在順序運行和並行直接切換。
4.補充
上述代碼中->
這是Java8裏新加入的特性lambda表達式,相當於無名稱的函數,匿名函數。一個簡單的例子是 Collections.sort(list, (x, y) -> y - x);
其中(x, y) -> y - x
是一個lambda表達式,輸入兩個參數x, y,返回值 y - x。->
起分隔作用。,Java會自動翻譯,這其實也有點類似JS中ES6的箭頭函數。
參考資料:https://www.runoob.com/java/java8-streams.html