1. 程式人生 > >使用Java Stream,提取集合中的某一列/按條件過濾集合/求和/最大值/最小值/平均值

使用Java Stream,提取集合中的某一列/按條件過濾集合/求和/最大值/最小值/平均值

不得不說,使用Java Stream操作集合實在是太好用了,不過最近在觀察生產環境錯誤日誌時,發現偶爾會出現以下2個異常: 1. java.lang.NullPointerException 2. java.util.NoSuchElementException 因此本篇部落格總結下使用Java Stream的部分場景以及如何避免上述的2個異常: 1. 提取集合中的某一列(普通提取、去重) 2. 按條件過濾集合 3. 求和 4. 最大值/最小值/平均值 ## 1. 資料準備 首先定義下Friend類: ```java package com.zwwhnly.springbootaction.model; import lombok.Data; import java.math.BigDecimal; @Data public class Friend { /** * 姓名 */ private String name; /** * 年齡 */ private Integer age; /** * 身高 */ private Long height; /** * 所在城市 */ private String city; /** * 體重 */ private BigDecimal weight; public Friend(String name, Integer age, Long height, String city, BigDecimal weight) { this.name = name; this.age = age; this.height = height; this.city = city; this.weight = weight; } } ``` 然後初始化以下資料供後面使用: ```java public static List getFriendList() { List friendList = new ArrayList<>(); friendList.add(new Friend("小周", 28, 175L, "鄭州", new BigDecimal("101.5"))); friendList.add(new Friend("小吳", 28, 170L, "洛陽", new BigDecimal("111.5"))); friendList.add(new Friend("小鄭", 29, 176L, "鄭州", new BigDecimal("121.5"))); friendList.add(new Friend("小王", 29, 180L, "北京", new BigDecimal("130"))); friendList.add(new Friend("小趙", 27, 178L, "蘇州", new BigDecimal("140"))); friendList.add(new Friend("小錢", null, null, "杭州", new BigDecimal("150"))); return friendList; } ``` ## 2. 提取集合中的某一列 ### 2.1 普通提取 比如,我們需要提取出所有朋友的姓名,可以使用Stream的map()方法,實現程式碼如下所示: ```java List friendList = getFriendList(); List nameList = friendList.stream().map(Friend::getName).collect(Collectors.toList()); nameList.forEach(name -> System.out.println(name)); ``` 輸出結果: > 小周 > > 小吳 > > 小鄭 > > 小王 > > 小趙 ### 2.2 提取後去重 比如,我們需要提取出所有朋友的年齡,但是需要去重,可以使用Stream的distinct()方法,實現程式碼如下所示: ```java List friendList = getFriendList(); List ageList = friendList.stream().map(Friend::getAge).distinct().collect(Collectors.toList()); ageList.forEach(age -> System.out.println(age)); ``` 輸出結果: > 28 > > 29 > > 27 ## 3. 按條件過濾集合 比如,我們需要獲取所有朋友中年齡在29歲以下,並且身高在170以上的朋友,可以呼叫`filter`方法,實現程式碼如下所示: ```java List friendList = getFriendList(); List youngPeople = friendList.stream() .filter(friend -> friend.getAge() != null && friend.getAge() < 29 && friend.getHeight() != null && friend.getHeight() > 170L) .collect(Collectors.toList()); System.out.println(youngPeople); ``` 輸出結果: > Friend(name=小周, age=28, height=175, city=鄭州, weight=101.5) > > Friend(name=小趙, age=27, height=178, city=蘇州, weight=140) ## 4. 求和 ### 4.1 Integer,Long,Double 比如,我們需要計算出所有朋友的年齡之和,可以呼叫`mapToInt`方法,實現程式碼如下所示: ```java List friendList = getFriendList(); int ageSum = friendList.stream().filter(friend -> friend.getAge() != null).mapToInt(Friend::getAge).sum(); System.out.println(ageSum); ``` 輸出結果: > 141 **注意事項:** 因為我們的age欄位定義的是包裝型別`Integer`,但求和之後的返回型別為基本型別`int`,所以在呼叫`mapToInt`方法之前,一定要過濾掉年齡為`null`的資料,否則分分鐘拋異常。 比如,我們新增一條年齡為`null`的資料: ```java friendList.add(new Friend("小錢",null,178,"杭州")); ``` 然後,我們不過濾null資料,直接呼叫`mapToInt`方法,就會丟擲`java.lang.NullPointerException`異常: ```java List friendList = getFriendList(); int ageSum = friendList.stream().mapToInt(Friend::getAge).sum(); System.out.println(ageSum); ``` ![](https://images.zwwhnly.com/picture/2020/08/snipaste_20200914_211023.png) 如果欄位型別是`Long`或者`Double`,可以呼叫相應的`mapToDouble`、`mapToLong`,如下所示: ![](https://images.zwwhnly.com/picture/2020/08/snipaste_20200915_152806.png) ### 4.2 BigDecimal 和Integer、Long、Double型別不同,如果欄位型別是`BigDecimal`,求和的話需要呼叫`reduce`方法,使用方法如下所示: ```java List friendList = getFriendList(); BigDecimal weightSum = friendList.stream() .filter(friend -> friend.getWeight() != null) .map(Friend::getWeight) .reduce(BigDecimal.ZERO, BigDecimal::add); System.out.println(weightSum); ``` 輸出結果: > 754.5 **注意事項:** 為避免`java.lang.NullPointerException`異常,上面程式碼中的`.filter(friend -> friend.getWeight() != null)`也要記得加。 ## 5. 最大值/最小值/平均值 ### 5.1 Integer,Long,Double 比如,我們需要獲取所有朋友中身高的最大值,實現程式碼如下所示: ```java List friendList = getFriendList(); long heightMax = friendList.stream() .filter(friend -> friend.getHeight() != null) .mapToLong(Friend::getHeight) .max().orElse(0); System.out.println(heightMax); ``` 輸出結果: > 180 **注意事項:** 因為max()方法的返回值是`OptionalLong`型別,所以我們需要繼續呼叫`orElse()`方法設定個預設值,這裡不要直接使用`getAsLong()`方法,因為當集合為空時,會丟擲你肯定遇到過的`java.util.NoSuchElementException`異常: ```java long heightMax = friendList.stream() .filter(friend -> friend.getHeight() != null) .mapToLong(Friend::getHeight) .max().getAsLong(); ``` ![](https://images.zwwhnly.com/picture/2020/08/snipaste_20200915_181453.png) orElse()原始碼如下所示: ```java public long orElse(long other) { return isPresent ? value : other; } ``` getAsLong()原始碼如下所示: ```java public long getAsLong() { if (!isPresent) { throw new NoSuchElementException("No value present"); } return value; } ``` 類似地,獲取最小值的程式碼如下所示: ```java List friendList = getFriendList(); long heightMin = friendList.stream() .filter(friend -> friend.getHeight() != null) .mapToLong(Friend::getHeight) .min().orElse(0); System.out.println(heightMin); ``` 獲取平均值的程式碼如下所示: ```java List friendList = getFriendList(); double heightAverage = friendList.stream() .filter(friend -> friend.getHeight() != null) .mapToLong(Friend::getHeight) .average().orElse(0D); System.out.println(heightAverage); ``` ### 5.2 BigDecimal 比如,我們需要獲取所有朋友中體重的最大值,實現程式碼如下所示: ```java List friendList = getFriendList(); BigDecimal weightMax = friendList.stream() .filter(friend -> friend.getWeight() != null) .map(Friend::getWeight) .max(BigDecimal::compareTo) .orElse(BigDecimal.ZERO); System.out.println(weightMax); ``` 輸出結果: > 150 **注意事項:** 1)為避免出現`java.lang.NullPointerException`異常,注意過濾體重為null的資料 2)因為max()方法的返回值為`Optional`型別,所以我們需要繼續呼叫orElse()方法設定個預設值,這裡不要直接使用get()方法,因為當集合為空時,會丟擲你肯定遇到過的`java.util.NoSuchElementException`異常: ```java BigDecimal weightMax = friendList.stream() .filter(friend -> friend.getWeight() != null) .map(Friend::getWeight) .max(BigDecimal::compareTo) .get(); ``` ![](https://images.zwwhnly.com/picture/2020/08/snipaste_20200915_195338.png) get()方法原始碼如下所示: ```java public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } ``` 類似地,獲取最小值的程式碼如下所示: ```java List friendList = getFriendList(); BigDecimal weightMax = friendList.stream() .filter(friend -> friend.getWeight() != null) .map(Friend::getWeight) .min(BigDecimal::compareTo) .orElse(BigDecimal.ZERO); System.out.println(weightMax); ``` ## 6. 總結 使用Java Stream操作集合非常便利,但還是容易踩一些坑,比如文中提到的`java.lang.NullPointerException`異常和`java.util.NoSuchElementException`異常,所以使用時要多多注意,能不踩坑就不踩坑,就算踩坑,也別多次踩同一個坑。