使用Java Stream,提取集合中的某一列/按條件過濾集合/求和/最大值/最小值/平均值
阿新 • • 發佈:2020-09-17
不得不說,使用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`異常,所以使用時要多多注意,能不踩坑就不踩坑,就算踩坑,也別多次踩同一個坑。