別再這麼寫程式碼了,這幾個方法不香嗎?
阿新 • • 發佈:2021-02-25
JDK8 應該是 Java 中最堅挺一個版本,這個版本新增很多特性,讓我們開發起來多了很多便利。
不過最近 Review 專案程式碼的時候發現,雖然很多專案工程已經使用了 JDK8,但是工程程式碼卻很少使用到 JDK8 新特性、新方法。
如果單從程式碼正確性上來說,老方式寫法寫當然沒有什麼問題,那唯一的缺點其實就是程式碼行數比較多,比較繁瑣。
那同樣的需求,使用 JDK8 新方法,其實幾行程式碼就可以搞定,這樣程式碼就會變得非常簡潔。
今天就以三個比較常見的場景為例,教你幾招,使用 JDK8 `Map`新增的方法簡化程式碼開發。
下面就來看看這次即將用到 `Map`幾個新方法:
![](https://img2020.cnblogs.com/other/1419561/202102/1419561-20210225075251533-93379058.jpg)
> 歡迎關注我的公眾號:小黑十一點半,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的部落格:[studyidea.cn](https://studyidea.cn)
## 預防空指標問題
日常開發中我們通常會從 `Map`獲取元素,然後進行相關的業務處理,示例程式碼如下:
```java
Map map = new HashMap();
map.put("公號", "小黑十一點半");
map.put("主理人", "樓下小黑哥");
// 可能存在 NPE 問題
System.out.println(map.get("支付").toUpperCase());
```
如果就像示例程式碼直接處理,一旦 `Map`中相應元素不存在,那麼我們就會碰到空指標問題。
為了解決這個問題,通常我們可以先判斷一下元素是否為 `null`,如果不為 `null`,再做相應的業務處理。
```java
// 第一種 if 判空
String value = map.get("支付");
if (!Objects.isNull(value)) {
System.out.println(value.toUpperCase());
}
```
這種方式唯一劣勢就是程式碼處理上比較繁瑣,不是很簡潔。
所以針對這種情況,其實可以使用**條件運算子**,設定一個預設空值,從而避免後續處理髮生空指標。
```java
// 第一種 if 判空
String value = map.get("支付");
// 第二種 條件運算子
value = Objects.isNull(value) ? "" : value;
```
這種方式比較簡潔,所以日常開發中我比較喜歡用這種方式。
> ps: 這裡的前提,空字串對於業務沒有特殊意義。如果存在特殊意義,那就不能使用這種方式了。
那如果使用 JDK8 ,其實就很方便了,我們就可以使用 `Map#getOrDefault`直接代替條件運算子。
```java
// 等同於條件運算子的效果: Objects.isNull(value) ? "" : value;
String value = map.getOrDefault("支付","");
```
藉助 `Map#getOrDefault` 一行程式碼直接搞定,就是這麼簡單。
如果你還在使用 JDK8 之前的版本,沒辦法使用這個方法。沒關係,我們可以藉助 Apache **Common-Lang3** 提供的工具類 `MapUtils` 避免空指標。
```java
// Apache MapUtils
String value = MapUtils.getString(map, "支付", "");
```
`MapUtils`這個工具類相對於`Map#getOrDefault`有一個好處,針對傳入 `Map`為 `null` 的情況,可以設定預設值。
假設我們是從 `POJO`物件獲取 `Map` 引數,這個時候為了防止空指標,我們就需要提前做一個空指標的判斷。
不過如果使用 `MapUtils`,那我們就不需要判斷是否為 `null`,方法內部已經封裝這個邏輯。
```java
MapUtils.getString(pojo.getMap(),"支付", "");
```
## 巧用 computeIfAbsent
日常開發中,我們會碰到這類場景,需要一個鍵需要對映到多個值,這個時候我們可以使用 `Map>`這個結構。
此時新增元素的時候,我們需要做一些判斷,當內部元素不存在時候主動建立一個集合物件,示例程式碼如下:
```java
Map> map = new HashMap();
List classify = map.get("java框架");
if (Objects.isNull(classify)) {
classify = new ArrayList<>();
classify.add("Spring");
map.put("java框架", classify);
} else {
classify.add("Spring");
}
```
上面的程式碼比較繁瑣,到了 JDK8,`Map`新增一個 `computeIfAbsent`方法:
```java
default V computeIfAbsent(K key,
Function super K, ? extends V> mappingFunction) {
```
如果 `Map`中 `key` 對應的 `value` 不存在,則會將 `mappingFunction` 計算產生的值作為儲存為該 `key` 的 `value`,並且返回該值。否則不作任何計算,將會直接返回 `key` 對應的 value。
利用這個特性,我們可以直接使用 `Map#computeIfAbsent`一行程式碼完成上面的場景,示例程式碼如下:
```java
map.computeIfAbsent("java框架", key -> new ArrayList<>()).add("Spring");
```
那其實 `Map` 中還有一個方法 `putIfAbsent`,功能跟 `computeIfAbsent`比較類似。
那剛開始使用的時候,誤以為可以使用 `putIfAbsent`完成上面的需求:
```java
// ERROR:會有 NPE 問題
map.putIfAbsent("java框架", new ArrayList<>()).add("Spring");
```
那其實這是錯誤的,當 `Map` 中 `key` 對應 `value` 不存在的時候,`putIfAbsent`將會直接返回 `null`。
而 `computeIfAbsent`將會返回 `mappingFunction`計算之後的值,像上面的場景直接返回就是 `new ArrayList`。
這一點需要注意一下,切勿用錯方法,導致空指標。
最後針對上面這種一個鍵需要對映到多個值,其實還有一個更優秀的解決辦法,使用 Google Guava 提供的新集合型別 `Multiset`,以此快速完成一個鍵需要對映到多個值的場景。
示例程式碼如下:
```java
ArrayListMultimap