背景:
1.金融業中,通常存在對賬行爲。
2.對賬的本質是比較雙方交易記錄。
3.雙方都有的交易記錄,比對【狀態 & 金額 】,我們只撿重點說哈。
3.1.狀態金額均相同,記爲成功,反之則失敗。
4.一方有另一方無的,我們記爲長短款。
Java8中新特性,在本博中,主要應用 Function && Stream。
對賬,通常有上游數據[reList],與本地數據[loList]。我假設其數據結構爲List<Map>。
先定義一個BiFunction,兩個入參,一個返回值。remote爲上游數據,local爲本地數據。
代碼如下:
static BiFunction<List<Map>, List<Map>, Map> diff = (remote, local) -> { null };
通常我們嵌套循環,遍歷兩個集合。這種操作太傳統了。如果兩個集合長度均爲百萬級,那還是有點莽。
我們來取個巧,我們將其中一個集合轉爲字典。
爲什麼要將List轉成Map呢,機智的小夥伴們已經明白了。因爲可以把嵌套循環拆分,減少數量級。
我們只需要循環一個List,從其中取出訂單號,再根據訂單號[map的key]從字典[Map]中取出本地訂單[map的value]。
我們已經假設了數據結構,所以,我們把交易訂單號當作Map的key,交易訂單當作value。
我們可以利用Java8中的新特性。代碼如下:
Map<String,Map> localMap =
local.stream().collect(Collectors.toMap(map -> map.get("orderNo").toString(), Function.identity()));
Collectors.toMap(keyFunc, valFunc) 方法接收的參數爲Lambda表達式,作用很明顯就是把集合轉成字典【Map】。
至於字典的key,value要從哪來呢,從集合中的對象來嘛。怎麼來呢?
你自己寫個 Function 去實現吧,反正我讓你傳兩個 Function 分別用來實現key 跟 value。
所以 keyFunc 用來生成我們轉換後的Map的key的,valFunc大家都知道了,就是用來生成我們Map的value的。
map.get("orderNo").toString(),我能理解,是從訂單記錄中取訂單號,然後當作Map的key嘛。
那 Function.identity() 又是個啥呢?別急哈,這個就是說,你傳入的參數是啥,我就返回啥。
我們是把訂單當作Map的值的嘛,所以,你當然也可以把 Function.identity() 寫成 map -> map 呀!
到這裏,我們已經把一個List 轉成一個Map了。
可愛的槓精小夥伴就會說,你說這麼多,好複雜。我直接用循環遍歷List,再轉成Map一樣能實現,還簡單。
嗯嗯,其實我也會,代碼如下:
public Map toMap(List<Map> list){
Map reslut = new HashMap();
for (Map map: list){
reslut.put(map.get("orderNo"),map);
}
return reslut;
}
可是,本博是爲了跟大家嘮嗑嘮嗑新特性嘛。所以槓精,不要再反駁我㕸...嘿嘿!
好了,題歸正傳。我們繼續。
我們要遍歷上游數據,然後把上游數據跟本地數據進行比較。來循環走起來。
public void diff(List<Map> list){
/**
* 上一步將本地訂單,由List轉成了Map
*/
Map<Object,Map> loMaps = new HashMap();
for (Map reMap : list){
//reMap 爲上游訂單
Object orderNo = reMap.get("orederNo");
//loMap 本地訂單
Map loMap = loMaps.get(orderNo);
/**
* 吧啦吧啦,拿着上游訂單[reMap]跟本地訂單[loMap]去對比吧。
* 這個具體對比過程,就是對賬,對比金額,狀態,時間什麼的。
* 然後是對賬結果入庫或落地。
*/
}
}
對賬就完成了,哈哈哈,完美。 ... 等等,好像有哪裏不對。我們不是要用新特性的嗎?
稍等,讓我冥想1秒鐘,好了,代碼出來了:
public void diff(List<Map> list){
/**
* 上一步將本地訂單,由List轉成了Map
*/
Map<Object,Map> loMaps = new HashMap();
list.stream().forEach(reMap -> {
//reMap 爲上游訂單
Object orderNo = reMap.get("orederNo");
//loMap 本地訂單
Map loMap = loMaps.get(orderNo);
/**
* 吧啦吧啦,拿着上游訂單[reMap]跟本地訂單[loMap]去對比吧。
* 這個具體對比過程,就是對賬,對比金額,狀態,時間什麼的。
* 然後是對賬結果入庫或落地。
*/
});
}
機智的小夥伴已經發現了,這TM有什麼區別嗎?不是就是for換成了.stream().forEach嗎?
額,大胸弟,莫急。聽我慢慢港。list.stream() 就是新特性,將集合流化。
流氓化後就可以有很多騷操作了呀,至於具體有哪些騷姿勢,那就需要你自己去慢慢開發了。
... ... 額,不是流氓化,是流化 ... ...
其實我還是個有點節操的人的。既然都已經說到了 BiFunction ,怎麼可能不再嘮叨兩句呢?
上面也太散了,好像有點懵。那就最後再整理一下吧!
/**
* 對賬
* <ol>
* <li>remote 爲上游訂單集合</li>
* <li>local 爲上游訂單集合</li>
* <li>我們假設集合中的對象爲Map,當然,也可以是你的自定義JavaBean,譬如OrderInfo什麼的</li>
* </ol>
*/
static BiFunction<List<Map>, List<Map>, Map> diff = (remote, local) -> {
/**
* 將本地訂單,由List轉成Map
* <br>以訂單號爲key,以訂單信息爲value
*/
Map<String,Map> loMaps =
local.stream().collect(Collectors.toMap((map) -> map.get("orderNo").toString(), map -> map));
/**
* 將上游數據流化
*/
Stream<Map> stream = remote.parallelStream();
/**
* 對賬結果
*/
Map result = new ConcurrentHashMap();
stream.collect(Collectors.toList()).forEach(reMap->{
//reMap 爲上游訂單
Object orderNo = reMap.get("orederNo");
//loMap 本地訂單
Map loMap = loMaps.get(orderNo);
/**
* 吧啦吧啦,拿着上游訂單[reMap]跟本地訂單[loMap]去對比吧。
* 這個具體對比過程,就是對賬,對比金額,狀態,時間,長短款什麼的。
* 然後是對賬結果入庫或落地。
* 也可以將對賬結果存入result
* 如果有業務需要,將對賬結果響應出去,由調用者另行處理。
*/
});
/**
* 返回對賬結果
*/
return result;
};
小夥伴們,是不是覺得,你寫這個 BiFunction 還不如我寫個方法呢。確實有點道理哈!
我們是要用新特性嘛,所以老鐵,別太在意這些細節。
那麼,方法大家都知道怎麼調用,這個 BiFunction 要怎麼調用呢?
BiFunction 申明成變量,直接調用BiFunction對象的apply(obj1, obj2)。
BiFunction 與 Function 的區別是,前者有兩個入參,後者只有一個入參。
也許有小夥伴說了,只有一兩個入參,我要是需要五六個入參,不就GG了。
Java是面向對象編程的語言,你隨便整個對象,要啥屬性沒有?是吧!
請看代碼:
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Demo {
/**
* 對賬
* <ol>
* <li>remote 爲上游訂單集合</li>
* <li>local 爲上游訂單集合</li>
* <li>我們假設集合中的對象爲Map,當然,也可以是你的自定義JavaBean,譬如OrderInfo什麼的</li>
* </ol>
*/
static BiFunction<List<Map>, List<Map>, Map> diff = (remote, local) -> {
/**
* 將本地訂單,由List轉成Map
* <br>以訂單號爲key,以訂單信息爲value
*/
Map<String,Map> loMaps =
local.stream().collect(Collectors.toMap((map) -> map.get("orderNo").toString(), map -> map));
/**
* 將上游數據流化
*/
Stream<Map> stream = remote.parallelStream();
/**
* 對賬結果
*/
Map result = new ConcurrentHashMap();
/**
* 遍歷上游訂單,跑對賬業務
*/
stream.collect(Collectors.toList()).forEach(reMap->{
//reMap 爲上游訂單
Object orderNo = reMap.get("orederNo");
//loMap 本地訂單
Map loMap = loMaps.get(orderNo);
/**
* 吧啦吧啦,拿着上游訂單[reMap]跟本地訂單[loMap]去對比吧。
* 這個具體對比過程,就是對賬,對比金額,狀態,時間,長短款什麼的。
* 然後是對賬結果入庫或落地。
* 也可以將對賬結果存入result
* 如果有業務需要,將對賬結果響應出去,由調用者另行處理。
*/
});
/**
* 返回對賬結果
*/
return result;
};
public static void main(String[] args) {
//上游訂單
List<Map> re = new ArrayList();
//本地訂單
List<Map> lo = new ArrayList();
/**
* 不得了了,我們就假裝他們有幾百萬條數據吧
*/
System.out.println("開始對賬");
Map map = diff.apply(re, lo);
System.out.println("對賬完成");
System.out.println("對賬結果" + map);
}
}
好啦!代碼都貼完了 ... 我應該也沒啥好說的了。
不過有個細心的小夥伴又問我了,爲什麼你上面遍歷上游數據時一開始用 stream.forEach( ... 後來又用 stream.collect(Collectors.toList()).forEach( ... 。很好,這種細節都被你們發現了。
其實吧,關鍵還是在於流化方式。
{ Stream<Map> stream = remote.parallelStream(); } 這是一個並行流
{ Stream<Map> stream = remote.stream(); } 這是一個串行流
stream.collect(Collectors.toList()).forEach 是爲了在並行流下,保證線程安全。