Java 流(Stream)操作實例 - 篩選、映射、查找匹配
準備工作
構建一個測試類,通過測試類先初始化一個數據源,具體如下。
public class TestObject {
private String name;
private String sex;
private int age;
private String email;
private boolean isMng;
public TestObject() {
}
public TestObject(String name,String sex,int age,String email,boolean isMng){
this.name=name;
this.sex=sex;
this.age=age;
this.email=email;
this.isMng=isMng;
}
//... ...get、set方法就不貼出來了,多又麻煩
}
在測試類中定義初始化數據源
public class StreamOperation {
static List<TestObject> list = Arrays.asList(
new TestObject("Ron","M",10,"ron.zheng@tfschange.com",false),
new TestObject("KDS","W",10,"kds@qq.com",false),
new TestObject("BoDuo","W",30,"boduo@163.com",false),
new TestObject("CangJin","W",10,"cangjin@gmail.com",false),
new TestObject("XiaoZe","W",30,"xiaoze@hotmail.com",true),
new TestObject("James","M",10,"leblonjames@hotmail.com",true),
new TestObject("Allen","M",50,"allen.lei@tfschange.com",true),
new TestObject("Smith","M",10,"jr.smith@cel.com",true),
new TestObject("Wade","M",20,"dw.wade@cel.com",true),
new TestObject("Wade","M",20,"dw.wade@cel.com",false)
);
//... ...流操作
}
用謂詞篩選
Streams 接口支持 filter 方法,該操作會接受一個謂詞(一個返回 boolean 的函數)作爲參數,並返回一個包括所有符合謂詞的元素的流。比如我們需要篩選 isMng 爲 ture 的數據並打印名字就可以按照如下的方式處理。
/**
* @Comment 獲取Leader
* @Author Ron
* @return
*/
public static List<TestObject> getLeader() {
return list.stream().filter(TestObject::isMng).collect(Collectors.toList());
}
public static void main(String[] args) {
List<TestObject> leaders = getLeader();
leaders.stream().forEach(leader->System.out.println(leader.getName()));
}
篩選各異的元素
流還支持一個叫作 distinct 的方法,它會返回一個元素各異(根據流所生成元素的 hashCode 和 equals 方法實現)的流。例如,以下代碼會篩選出列表中所有的偶數,並確保沒有重複。
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println);
}
截短流
流支持 limit(n) 方法,該方法會返回一個不超過給定長度的流。所需的長度作爲參數傳遞給 limit。如果流是有序的,則最多會返回前 n 個元素。比如選出前 5 個 sex 爲 M 的對象並打印其名稱可以按如下的代碼操作。
list.stream().filter(u->u.getSex().equals("M")).limit(5).forEach(u->System.out.println(u.getName()));
如果我們需要選出前 5 個 sex 爲 M 的對象並按照名稱排序之後打印其名稱可以按如下的代碼操作。
list.stream()
.filter(u->u.getSex().equals("M"))
.limit(5)
.sorted(Comparator.comparing(TestObject::getName))
.forEach(u->System.out.println(u.getName()));
跳過元素
流還支持 skip(n) 方法,返回一個扔掉了前 n 個元素的流。如果流中元素不足 n 個,則返回一個空流。請注意, limit(n) 和 skip(n) 是互補的。例如,下面的代碼將會跳過篩選出來的第一個元素並打印名字。
list.stream()
.filter(u->u.getSex().equals("M"))
.sorted(Comparator.comparing(TestObject::getName))
.skip(1)
.forEach(u->System.out.println(u.getName()));
對流中每一個元素應用函數
流支持 map 方法,它會接受一個函數作爲參數。這個函數會被應用到每個元素上,並將其映射成一個新的元素(使用映射一詞,是因爲它和轉換類似,但其中的細微差別在於它是 “創建一個新版本” 而不是去“修改”)。
例如,下面的代碼把方法引用 TestObject::getName 傳給了 map 方法,來提取流中用戶的名稱並打印:
list.stream()
.map(TestObject::getName)
.collect(Collectors.toList())
.forEach(System.out::println);
因爲 getName 方法返回一個 String,所以 map 方法輸出的流的類型就是 StreamString。(搜索公衆號 Java 知音,回覆 “2021”,送你一份 Java 面試題寶典)
我們來再看一個例子,我們把方法引用 TestObject::getName 傳給了 map 方法,來提取流中用戶的名稱,然後再打印用戶名稱的長度。你可以像下面這樣,給 map 傳遞一個方法引用 String::length 來解決這個問題:
list.stream()
.map(TestObject::getName)
.map(String::length)
.collect(Collectors.toList())
.forEach(System.out::println);
檢查謂詞是否至少匹配一個元素
anyMatch 方法可以回答 “流中是否有一個元素能匹配給定的謂詞”。比如,你可以用它來看看用戶列表裏面是否有名稱爲 Ron 的對象可選擇:
if(list.stream().anyMatch(u->u.getName().equals("Ron"))){
System.out.println("Ron已經到了");
}
anyMatch 方法返回一個 boolean,因此是一個終端操作。
檢查謂詞是否匹配所有元素
allMatch 方法的工作原理和 anyMatch 類似,但它會看看流中的元素是否都能匹配給定的謂詞。比如,你可以用它來看看用戶是否都大於 10 歲。
if(list.stream().allMatch(u->u.getAge()>=10)){
System.out.println("很棒,都大於10歲");
}else{
System.out.println("原來都還沒發育");
}
和 allMatch 相對的是 noneMatch。它可以確保流中沒有任何元素與給定的謂詞匹配。比如,你可以用 noneMatch 重寫前面的例子:
if(list.stream().noneMatch(u->u.getAge()<10)){
System.out.println("很棒,都大於10歲");
}else{
System.out.println("原來都還沒發育");
}
anyMatch、 allMatch 和 noneMatch 這三個操作都用到了我們所謂的短路,這就是大家熟悉的 Java 中 && 和 || 運算符短路在流中的版本。
Optional 簡介
Optional<T>
類(java.util.Optional)是一個容器類,代表一個值存在或不存在。Java 8 的庫設計人員引入了Optional<T>
,這樣就不用返回衆所周知容易出問題的 null 了。Optional 裏面幾種可以迫使你顯式地檢查值是否存在或處理值不存在的情形。
-
isPresent() 將在 Optional 包含值的時候返回 true, 否則返回 false。
-
ifPresent(Consumer block) 會在值存在的時候執行給定的代碼塊。
-
T get() 會在值存在時返回值,否則拋出一個 NoSuchElement 異常。
-
T orElse(T other) 會在值存在時返回值,否則返回一個默認值。
查找元素
findAny 方法將返回當前流中的任意元素。它可以與其他流操作結合使用。
例如,我們需要顯示的檢查是否存在一個名爲‘Ron’的人並顯示其名稱就可以按照如下的代碼操作。
list.stream()
.filter(u->u.getName().equals("Ron"))
.findAny()
.ifPresent(u->System.out.println(u.getName()));
流水線將在後臺進行優化使其只需走一遍,並在利用短路找到結果時立即結束。
查找第一個元素
有些流有一個出現順序(encounter order)來指定流中項目出現的邏輯順序(比如由 List 或排序好的數據列生成的流)。對於這種流,你可能想要找到第一個元素。爲此有一個 findFirst 方法,它的工作方式類似於 findany。
例如我們需要找到第一個 isLeader 爲 ture 的對象並打印其名字,就可以按照如下的代碼操作。
list.stream()
.filter(u->u.isLeader())
.findFirst()
.ifPresent(u->System.out.println(u.getName()));
何時使用 findFirst 和 findAny
你可能會想,爲什麼會同時有 findFirst 和 findAny 呢?答案是並行。找到第一個元素在並行上限制更多。如果你不關心返回的元素是哪個,請使用 findAny,因爲它在使用並行流時限制較少。
參考:Java8 實戰
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/bBLu_SvAJQzoax_hOYppCw