Java8-新特性 && 金融對賬應用

背景:

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                   是爲了在並行流下,保證線程安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章