摘要:最近在开发过程中,因为重写了TreeMap的比较器,而引发了一些有趣的问题。
需求背景
1、给一个无序的map,按照value的值进行排序,value值越小,排在越前面。
2、key和value都不为null
3、value可能相同
4、返回结果为一个相同的有序map
代码如下所示:
1 | // 假设,key=商品id,value=商品剩余库存 |
到这里,大家可以先想想,如果是你会怎么解决?
我的解决思路
1、使用TreeMap,因为TreeMap可以对元素进行排序
2、重写TreeMap的比较器
代码如下所示:
1 | // 承接上面的代码 |
运行后的结果为:
1 | {1=10, 2=20} |
what?为什么我们添加了3个元素,结果少了一个呢?
TreeMap putAll源码分析
让我们来看看 putAll 的具体过程
1、分析 TreeMap.putAll
源码如下所示:
1 | public void putAll(Map<? extends K, ? extends V> map) { |
从上面源码,不难看出,我们的数据符合 流程二,但是不符合 流程二-2,所以我们会执行父类的 putAll 方法,即流程三。
2、分析 AbstractMap.putAll
TreeMap 继承 AbstractMap,所以 super.putAll(map),执行的 putAll 为 AbstractMap 的 putAll 方法,源码如下所示:
1 | public void putAll(Map<? extends K, ? extends V> m) { |
这段代码简单,就是一个遍历添加元素的。
但是有一个问题,这里的 put 方法执行的是谁的 put 方法呢?
- 1、AbstractMap.put
- 2、TreeMap.put
这里大家可以先思考1分钟,然后再继续往下看。
答案是:
1 | 执行的是 TreeMap.put |
回答错误 or 不知道真实原因的小伙伴,可以去网上搜搜答案,这里是一个很重要的基础知识点哦。
3、分析 TreeMap.put
源代码如下所示:
1 | public V put(K key, V value) { |
我们结合上面的源码和我们自定义的排序器,就可以发现以下问题:
1、我们比较的是两个 value 的大小,而 value 可能是一样的。
这种情况下,就会覆盖原来的值,这个就是我们执行 putAll 后,元素缺失的原因了。
好了既然问题找到了,那如何解决这个问题呢?
如果是你,你会怎么解决呢?可以花一分钟时间思考一下,再看后面的内容。
4、解决 TreeMap.putAll,元素缺失的问题
我当时想到最直接的方案就是,在 value 相等的情况下,不返回 0,返回1 or -1,这样就可以最简单、最快捷的解决这个问题了。
修改后的代码如下所示:
1 | // 这里换了一种写法,是java8的特性,简化了代码(为了偷懒) |
运行后的结果为:
1 | {3=10, 1=10, 2=20} |
我们可以发现,3个值都有了,并且是有序的,完美符合需求!好了,关机下班!
然而事情并没有结束(大家可以想一下,这样写会有什么问题呢?)!
新的问题出现
第二天,高高兴兴的写着业务代码、调试逻辑,突然一个空指针的报错,出现了。这也太常见了吧,3分钟内解决!
排查了半天,发现又回到了昨天的修改的那段逻辑了。
1、TreeMap.get 获取不到值
简化版代码如下所示:
1 | // 假设,key=商品id,value=商品剩余库存 |
运行后的结果为:
1 | {3=10, 1=10, 2=20} |
这个结果令我百思不得其解,只能看看源码咯。
2、分析 TreeMap.get
源码如下所示:
1 | public V get(Object key) { |
从上面的源码,我们可以发现,问题肯定就是出现在 getEntryUsingComparator 方法里了。
2、分析 TreeMap.getEntryUsingComparator
源码如下所示:
1 | final TreeMap.Entry<K,V> getEntryUsingComparator(Object key) { |
结合上面的源码,和我们之前自定义的比较器,我们不难发现问题出现在哪里:
1 | 自定义比较器,没有返回0的情况 |
问题找到了,解决吧!
加班中,今天到此结束!
啊杰,在这里谢谢大家的观看,下次再见。
- 本文作者: th3ee9ine
- 本文链接: https://www.blog.ajie39.top/2022/01/16/重写TreeMap的比较器(Comparator)引发的问题(源码分析)/
- 版权声明: 本博客所有文章除特别声明外,均采用 LICENSE 下的许可协议。转载请注明出处!