为家庭地位而战的:怎样合并两个map

背景
  • 在最近做课程需求中,有一个小需求的实现,需要从第三方直播平台拉取数据,对某一个学生的数据进行综合统计。 总之,在这里边就涉及到一个小算法:合并两个Map。 其Map的结构是Map<long,Integer>,之所以需要合并,是因为一个用户的综合统计值,存在于多批数据中。也因此,这个骚操作的具体要求就是:合并两个Map,取key的并集最大值,如果有重复的key值,其value的处理方案为:MapA.value+MapB.value
  • say say 为啥又灌了这篇水文吧。话说,别人家的家庭地位可能是以经济成分来算,或者是别的,反正我是不知道。但我的家庭地位,总是莫名其妙的来源于 技术 咖位。当然啦,我是从来没胜利过,除了这一次的map合并。PS: 不排除这两天李烦烦生病了, 脑袋不清醒。 所以,这次的胜利,真是天时+地利+人和,哈哈哈哈,我就建立我的快乐到别人的痛苦之上,真的,就这一次。 神会原谅我的,阿门,遂作文纪念。
实现经过
  • 第一种方案
  1. 思路: 遇到这个需求时,我的第一反应是把两个Map的key值收集起来,然后放入一个Set里获取key的最大并集,然后遍历Set,组合两个map的数据,存入最终的返回map中
  2. 思考: 猛然间,想了想时间复杂度和空间复杂度的问题。(主要是老被对象吐槽代码写的不好,就特别想优化自己的每一行代码) 如果是以这种方式实现,那我,要初始化一个Set,还要初始化一个返回Map,而如果这个map不设置初始大小,势必会面临着扩容。 所以,以两个原Map的大小,并结合扩容因子设置,初始化这个Map的大小。 这样的空间复杂度就是O(n),时间复杂度也是O(n)
  3. 强行加戏: 有没有其他方案,可以从时间复杂度或者空间复杂度上来说,更为优良的方案呢。 如果我不新增临时变量,是不是也可以实现。因此,我想到了循环。
  • 第二种方案
  1. 思路: 循环一个mapA,从另一个mapB中查看是否包含当前key值,如果不包括,则把当前的key,添加到mapB中;如果包括,则按照规则覆盖mapB中key的value值
  2. 思考: 如果是利用这样的循环,那么,我就可以不新增临时变量了。而且,相对于第一种,尽管时间复杂度均为O(n),但就其数量来讲-循环次数,这种循环的次数少于第一种遍历set的次数。问题是:当一个map中不存在key值,而往其中添加时,往往会引发扩容机制,怎么样,能够减少扩容的次数呢?
  3. 解决方案: 扩容是当容量达到75%时,会增加一倍的容量。如果我循环的mapA是长度较短的一个,那么当mapB因为新增key值发生扩容,最多也只会有一次。
  • 综上: 是我想太多,我也这样说。可我忍不住,就爱想太多。 我把我这一堆猛如虎的骚操作,第一时间跟我对象-李烦烦分享了。本想获得一顿夸,谁知道:理解没啥大问题,但不用想这么复杂。 我靠靠,当时的心情,难以言表。这是我想复杂了吗?这是我想复杂了吗?这是我想复杂了吗?让我搞大技术,大架构是没啥大指望的,我就想着能够在细节上,偶尔能胜我对象一点。唉,我怎么这么难呢。 。。。。。。。以为我就这样败了么,事实并没有。。。。。。。。接着记录我这次大捷。。。。。。。
代码

这是我的代码,因为业务关系,在使用中我会根据数据情况,选取最小长度的map作为mapA:

mapA.forEach((k, v) -> {
             if (mapB.containsKey(k)) {
                 Integer bValue = mapB.get(k);
                 mapB.put(k, bValue + v);
             } else {
                 mapB.put(k, v);
             }
         });

后来,李烦烦看了我给他发的这段代码,给我整了以下的代码:

        return Stream.of(mapA.entrySet(), mapB.entrySet())
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (m1,m2) -> m1 + m2));

好吧,其实我也不知道这个里边具体怎么实现的,也没有看出来这段代码跟我写的瞎循环相比有什么优良性。怪我水平low,不过出于李烦烦的崇拜,我最终提交的代码,就是如上的。

败局转胜

为了方法的健壮性和复用性(我自己的业务场景,不存在参数为空的情况),我需要对参数map进行非空校验。哈哈哈哈,正所谓:智者千虑,必有一失;愚者千虑,必有一得。我写的非空校验是这样的:

  if(CollectionUtils.isEmpty(mapA)){
            return mapB;
        }
        if(CollectionUtils.isEmpty(mapB)){
            return mapA;
        }

李烦烦说我应该将这两个非空校验写在一起,因为它们的语义相同,就应该写成:

if(CollectionUtils.isEmpty(mapA) || CollectionUtils.isEmpty(mapB)){
            return null;
        }

哈哈哈,这么大个bug被抓住,不大肆嘲讽简直对不住我多次惨败的心。太开心了。。。。。。。这样写在一起,当其中一个为空map的时候,全部返回了null。 然而我的目的是取两个map合并的最大并集,其中一个为null为空,不代表另一个也为空为null。 只有两个参数都为空时,才能返回一个空map。 所以综合考虑, 就应该分别校验更合理,嘿嘿嘿嘿嘿嘿。

获胜感言
  • 根据我屡败屡战的经验,从战略上胜出李烦烦的可能不大,但从细节上略胜一筹还是可期可盼的。要对未来的家庭地位上升有信心,道路虽曲折,但局势肯定是明朗的,哼哼哈嘿,叫我大王。
  • 另外,对于代码而言。我觉得技术本身没有价值。也因此代码的第一要义,应该是实现业务需求,运行准确、稳定。其次是,性能优良;再者是代码优雅。 后两者,往往相辅相成。觉得自己又进步了一点点,会考虑每行代码怎么写运行效率更高。也许是由于知识储备,即使我想了也没对,但有了想法和实践,就离正确的不远了。加油。。。。。PS:李烦烦说其实合并这个还有很多个方法,让我写总结的时候想想。。。。。还有啥?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值