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                   是为了在并行流下,保证线程安全。

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