何用Java8 Stream API進行數據抽取與收集


原稿於3.2日發布,然而事情並沒完,我發現必須得補充一個常見的坑,為了防止大家采坑,我在更新了本文的后兩段。

上一篇中我們通過一個實例看到了Java8 Stream API 相較於傳統的的Java 集合操作的簡潔與優勢,本篇我們依然借助於一個實際的例子來看看Java8 Stream API 如何抽取及收集數據。
備注:上一篇內容:如何用Java8 Stream API找到心儀的女朋友

目標 & 背景

我們以“處理訂單數據”為例,假設我們的應用是一個分布式應用,有"訂單應用","物流應用","商品應用”等都是獨立的服務。本次我們的目的需要展示訂單列表完整數據:

  • 1.查詢訂單列表。
  • 2.批量查詢物流信息。
  • 3.將物流信息填充到訂單主信息中。

假設我們定義了一個訂單類,具有幾個關鍵的屬性:訂單號,狀態,訂單價,快遞信息。如下所示:

class Order{
    String orderSeq;
    String status;
    double totalPrice;
    String expressInfo;
    // 省略get,set及hashCode等方法
}    

我們定義了一個快遞信息類,幾個關鍵的屬性:訂單號,物流公司,物流單號,物流狀態。如下所示:

class ExpressInfo{
    String orderSeq;
    String expressName;
    String expressNo;
    String createTime;
    String statusInfo;
    // 省略get,set及hashCode等方法
}

Java7 實現

獲取訂單列表 & 抽取訂單號

   List<Order> orderList = getOrderList();
    // 抽取 訂單號
    List<String> orderSeqList = new ArrayList<>();
    for (Order order : orderList) {
        orderSeqList.add(order.getOrderSeq());
    }

這里我們獲取了訂單列表orderList,此時expressInfo里邊是沒有數據的。這里抽取單號依然是Java傳統的寫法。

批量查詢快遞信息 & 組裝 訂單-快遞信息 map

由於我們是通過調用遠程服務來獲取快遞信息,為了減少網絡通信次數,我們采取批量查詢的方式。這也是為什么,上一步中我們要抽取訂單號
下面我們來獲取快遞信息

// 調用遠程服務,
List<ExpressInfo> expressInfos = RpcGetExpressInfoBatch(orderSeqList);
// 組裝 訂單-快遞 關系map
Map<String,String> orderExpressMap = new HashMap<>();
for(ExpressInfo e: expressInfos){
    orderExpressMap.put(e.getOrderSeq(),e.getStatusInfo());
}

這里組裝map,也依然是Java7常用的寫法。

組合數據,將快遞信息填充進訂單

for(Order order:orderList){
    String expressInfo = orderExpressMap.get(order.getOrderSeq());
    order.setExpressInfo(expressInfo);
}

至此,我們使用Java7 的寫法,完成了開篇設定的目標。下面我們看Java8的寫法

Java8 實現

獲取訂單列表 & 抽取訂單號

// 獲取列表
List<Order> orderList = getOrderList();
// 抽取單號
List<String> orderSeqs = orderList.stream()
        .map(Order::getOrderSeq)
        .collect(Collectors.toList());

這里我們使用了stream.map,在map()中,我們的寫法是Order::getOrderSeq表示調用Order對象的getOrderSeq()方法來抽取訂單號。
這里的::叫“方法引用”,是Java8中的新寫法。
map()后面緊跟的是collect收集器,他將抽取的數據toList(),於是我們得到了最終的List

批量查詢快遞信息 & 組裝 訂單-快遞信息 map

下面我們仍然是通過遠程調用來獲取快遞信息,然后使用Java8的語法建立一個 訂單-快遞 關聯信息的map。

List<ExpressInfo> expressInfos = RpcGetExpressInfoBatch(orderSeqList);
Map<String,String> orderExpressMap =expressInfos.stream()
        .collect(Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo));

這里代碼比Java7的要少吧,且一目了然,這里用strean().collect來收集數據,收集成什么形式呢?看名知意,Collectors.toMap收集成Map,收集什么數據呢?toMap()中,寫了ExpressInfo::getOrderSeqExpressInfo::getStatusInfo,即:抽取orderSeq作為key,statusInfo作為value。

通過這里的Collectors.toMap收集器,我們很方便的獲得了 訂單-物流關系數據map

組合數據,將快遞信息填充進訂單

經過上面的兩步,我們得到了符合我們要求的數據,現在我們需要將快遞信息填充進訂單,代碼如下:

orderList.stream().forEach(o -> o.setExpressInfo(orderExpressMap.get(o.getOrderSeq())));

你沒看錯,就只有這么一行。

補充說明: key 重復的處理

上面的代碼中,我們使用如下代碼來從list中收集Map:

Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo))

這里如果expressInfos這個list中有重復數據,那么orderSeq就會有重復的,這種情況下就會報一個錯:

java.lang.IllegalStateException: Duplicate key
想模擬這個錯誤很簡單,往expressInfos中add兩條一樣的數據,運行上面的抽取map代碼就會報錯。
為了解決這個問題,我們需要這樣寫:

List<ExpressInfo> expressInfos = RpcGetExpressInfoBatch(orderSeqList);
 Map<String,String> orderExpressMap =expressInfos.stream()
          .collect(Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo,(v1,v2)->v1));

這里,我們主要的變動是這一句:

Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo,(v1,v2)->v1)

我們在在Collectors.toMap中增加了一個lumda表達式:

(v1,v2)->v1

意思是當有重復數據v1,v2出現的時候,我們選擇v1。當然,你可以根據你的情況選擇v2。
選擇v1,或者v2,需要根據你的業務來權衡。比如list中的數據按照時間先后排序的,我們取最新的就選v2,反之取v1。

總結

本節,我們使用Java8 Stream API,完成了數據的抽取和收集,使用了map(),和collect()來完成數據的抽取和收集,並了解了兩種收取方式toListtoMap。同時我們也知道應對list中有重復數據導致報錯的問題。所以,以后如果有人問你"Java8 stream 如何獲取對象的某個屬性list啊?",“java8 stream 如何獲取指定數據組裝成map啊?”,你就可以把本文中的方法告訴他了。

除此之外,Java8 Streap API 還有分組 等功能,后面再說。你也可以關注我的公眾號,第一時間收到推送。

alt 逃離沙漠公眾號

Java8系列- 如何用Java8 Stream API找到心儀的女朋友
SpringMVC是怎么工作的,SpringMVC的工作原理
Mybatis Mapper接口是如何找到實現類的-源碼分析
小程序雲開發:菜鳥也能全棧做產品
CORS詳解,CORS原理分析
工作6年,失業19天

Docker & k8s 系列一:快速上手docker
Docker & k8s 系列二:本機k8s環境搭建
Docker & k8s 系列三:在k8s中部署單個服務實例


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM