1、什麼是Stream
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數據。Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。Stream API可以極大提高Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。
總之,中間操作只是創建另一個流,不會執行任何處理,直到最終操作被調用。一旦最終操作被調用,則開始遍歷所有的流,並且相關的函數會逐一應用到流上。中間操作是惰性操作,所以,流支持惰性,下面是一些函數的分類。
2、流的使用
2.1、獲取Stream
在使用流之前,首先需要擁有一個數據源,並通過StreamAPI提供的一些方法獲取該數據源的流對象。數據源可以有多種形式:
(1)集合
這種數據源較為常用,通過stream()方法即可獲取流對象:
Listlist = new ArrayList<>();
Streamstream = list.stream();
(2)數組
通過Arrays類提供的靜態函數stream()獲取數組的流對象:
String[] array = {"a","b","c"};
Streamstream = Arrays.stream(array);
(3)值
直接將幾個值變成流對象:
Streamstream = Stream.of("a", "b", "c");
2.2、forEach
對流中的每個元素執行一些操作。
Listlist = new ArrayList<>(Arrays.asList("a","b","c"));
//寫法一
list.stream().forEach(x -> {
System.out.println(x);
});
//寫法二
list.stream().forEach(System.out::println);
2.3、map
接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素
Listlist = new ArrayList<>(Arrays.asList("a","b","c"));
//寫法一
list.stream().forEach(x -> {
System.out.println(x);
});
//寫法二
list.stream().forEach(System.out::println);
2.4、flatMap
接收一個函數作為參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流。
Listlist = new ArrayList<>(Arrays.asList("a,1","b,2","c,3"));
ListnewList = list.stream().flatMap(x->{
String[] array = x.split(",");
return Arrays.stream(array);
}).collect(Collectors.toList());
結果:
[a, 1, b, 2, c, 3]
2.5、reduce()
通過一個二進制操作將流中的元素合併到一起。
List
//寫法一,累加不帶初始值
Optional accResult = list.stream().reduce((acc, item) -> {
acc += item;
return acc;
});
輸出6
//寫法一,累加帶初始值10
int result1 = list.stream().reduce(10, (acc, item) -> {
acc += item;
return acc;
});
輸出16
2.6、filter()
過濾元素
Listlist = new ArrayList<>(Arrays.asList(1, 2, 3,4));
ListnewList = list.stream().filter(x -> x >=3).collect(Collectors.toList());
輸出[3,4]
2.7、distinct()
去除重複元素
Listlist = new ArrayList<>(Arrays.asList(1, 1, 1,2,2));
ListnewList = list.stream().distinct().collect(Collectors.toList());
輸出結果
[1,2]
2.8、collect()
將流中的元素傾倒入某些容器
(1)toList
將元素轉成List,很簡單,上面的實例很多
(2)toSet
將元素轉成Set
Listlist = new ArrayList<>(Arrays.asList(1, 1, 1, 2, 2));
Setset = list.stream().collect(Collectors.toSet());
(3) toMap
將元素轉成Map
用法可以如下
public MapgetIdNameMap(List accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, Account::getUsername));
}
收集成實體本身map
代碼如下:
public MapgetIdAccountMap(List accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account));
}
account -> account是一個返回本身的lambda表達式,其實還可以使用Function接口中的一個默認方法代替,使整個方法更簡潔優雅:
public MapgetIdAccountMap(List accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, Function.identity()));
}
重複key的情況
代碼如下:
public MapgetNameAccountMap(List accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()));
}
這個方法可能報錯(java.lang.IllegalStateException: Duplicate key),因為name是有可能重複的。toMap有個重載方法,可以傳入一個合併的函數來解決key衝突問題:
public MapgetNameAccountMap(List accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2));
}
這裡只是簡單的使用後者覆蓋前者來解決key重複問題。
指定具體收集的map
toMap還有另一個重載方法,可以指定一個Map的具體實現,來收集數據:
public MapgetNameAccountMap(List accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));
}
2.8、min()
根據一個比較器找到流中元素的最小值。
BigDecimal min = planList.stream().min((a,b)->a.getPrice().compareTo(b.getPrice())).get().getPrice();
2.9、max()
根據一個比較器找到流中元素的最大值。
BigDecimal max = planList.stream().max((a,b)->a.getPrice().compareTo(b.getPrice())).get().getPrice();
2.10、count()
計算流中元素的數量。
Listlist = new ArrayList<>(Arrays.asList(14, 23, 3,10, 1,6));
long min = list.stream().distinct().count();
---------------------
閱讀更多 程序猿猩球 的文章