中招了,重写TreeMap的比较器引发的问题...(下)

简介: 中招了,重写TreeMap的比较器引发的问题...(下)

这种情况下,就会覆盖原来的值,这个就是我们执行 putAll 后,元素缺失的原因了。


image.png


好了既然问题找到了,那如何解决这个问题呢?


如果是你,你会怎么解决呢?可以花一分钟时间思考一下,再看后面的内容。


4、解决 TreeMap.putAll,元素缺失的问题


我当时想到最直接的方案就是,在 value 相等的情况下,不返回 0,返回1 or -1,这样就可以最简单、最快捷的解决这个问题了。


修改后的代码如下所示:


// 这里换了一种写法,是java8的特性,简化了代码(为了偷懒)
Map<Long, Integer> treeMap2 = new TreeMap<>((key1, key2) -> {
    // 1、如果v1等于v2,则值为0
    // 2、如果v1小于v2,则值为-1
    // 3、如果v1等于v2,则值为1
    Integer value1 = map.get(key1);
    Integer value2 = map.get(key2);
    int result = value1.compareTo(value2);
    if (result == 0) {
        return -1;
    }
    return result;
});
treeMap2.putAll(map);
System.out.println(treeMap2);


运行后的结果为:


{3=10, 1=10, 2=20}


我们可以发现,3个值都有了,并且是有序的,完美符合需求!好了,关机下班!


image.png


然而事情并没有结束 (大家可以想一下,这样写会有什么问题呢?)


新的问题出现


第二天,高高兴兴的写着业务代码、调试逻辑,突然一个 空指针 的报错,出现了。这也太常见了吧,3分钟内解决!


image.png


排查了半天,发现又回到了昨天的修改的那段逻辑了。


1、TreeMap.get 获取不到值


简化版代码如下所示:


// 假设,key=商品id,value=商品剩余库存
Map<Long, Integer> map = new HashMap<>();
map.put(1L, 10);
map.put(2L, 20);
map.put(3L, 10);
// 排序
Map<Long, Integer> treeMap2 = new TreeMap<>((key1, key2) -> {
    Integer value1 = map.get(key1);
    Integer value2 = map.get(key2);
    int result = value1.compareTo(value2);
    if (result == 0) {
        return -1;
    }
    return result;
});
treeMap2.putAll(map);
System.out.println(treeMap2);
// 获取商品1的剩余数量
Integer quantity = treeMap2.get(1L);
System.out.println(quantity);


运行后的结果为:


{3=10, 1=10, 2=20}
null


这个结果令我百思不得其解,只能看看源码咯。


2、分析 TreeMap.get


源码如下所示:


public V get(Object key) {
    // 根据key获取节点
    TreeMap.Entry<K,V> p = getEntry(key);
    // 节点为空则返回null,否则返回节点的 value 值
    return (p==null ? null : p.value);
}
final TreeMap.Entry<K,V> getEntry(Object key) {
    // 一、如果比较器不为空,则执行一下逻辑
    if (comparator != null)
        // 1、使用自定义比较器取出key对应的节点
        return getEntryUsingComparator(key);
    // 二、如果比较器为空,且key为null,则抛空指针异常
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
    Comparable<? super K> k = (Comparable<? super K>) key;
    TreeMap.Entry<K,V> p = root;
    // 三、取出key对应的节点
    while (p != null) {
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}


从上面的源码,我们可以发现,问题肯定就是出现在 getEntryUsingComparator 方法里了。


2、分析 TreeMap.getEntryUsingComparator


源码如下所示:


final TreeMap.Entry<K,V> getEntryUsingComparator(Object key) {
    // 一、将key转换成对应的类型
    @SuppressWarnings("unchecked")
    K k = (K) key;
    // 二、获取比较器
    Comparator<? super K> cpr = comparator;
    // 三、判断比较器是否为空
    if (cpr != null) {
        // 1、遍历map,取出key对应的节点对象
        TreeMap.Entry<K,V> p = root;
        while (p != null) {
            int cmp = cpr.compare(k, p.key);
            // 2、如果小于0,则将左节点的值赋值给p
            if (cmp < 0)
                p = p.left;
            // 3、如果大于0,则将右节点的值赋值给p
            else if (cmp > 0)
                p = p.right;
            else
                // 4、如果等于0,则返回p节点
                return p;
        }
    }
    return null;
}


结合上面的源码,和我们之前自定义的比较器,我们不难发现问题出现在哪里:


image.png


自定义比较器,没有返回0的情况


问题找到了,解决吧!

相关文章
|
4月前
|
Java
JAVA中比较对象是否相等的方式是什么?为什么重写equals就一定要重写hashcode?百天百题(3/100)
JAVA中比较对象是否相等的方式是什么?为什么重写equals就一定要重写hashcode?
|
6月前
|
存储
集合框架知识总汇之(Set集合)
集合框架知识总汇之(Set集合)
28 0
|
6月前
|
测试技术 容器
集合框架知识总汇之(list集合)
集合框架知识总汇之(list集合)
19 0
|
8月前
|
安全 Java 数据库连接
Java常用类库中(ThreadLocal、Comparable比较器、AutoCloseable、Optional空处理)附带相关面试题
1.ThreadLocal线程独立,2.Comparable比较器与Comparetor,3.AutoCloseable接口,4.Optional空处理
45 0
|
12月前
|
Java
《牛客刷题计划》旧键盘(Java数据结构Set和Map的使用)
《牛客刷题计划》旧键盘(Java数据结构Set和Map的使用)
|
存储 安全 算法
Java常用集合总结
1.集合的理解和好处 1)我们先分析一下使用数组的弊端: ①长度开始时必须指定,并且一旦指定,不能更改 ②保存的元素必须为同一类型 ③增加/删除元素比较麻烦 2)集合的好处 ①可以动态保存任意多个对象,使用比较方便 ②提供了很多方便的操作对象的方法:add、remove、set、get等 3)集合的框架体系如下: ①单列集合
Java常用集合总结
Java集合相关学习——元素排序两大接口Comparable和Comparator的应用及区别
Java集合相关学习——元素排序两大接口Comparable和Comparator的应用及区别
Java集合相关学习——元素排序两大接口Comparable和Comparator的应用及区别
|
小程序 Java
Java——使用集合实现简单的斗地主发牌功能(两种方式简单粗暴!!!)
Java——使用集合实现简单的斗地主发牌功能(两种方式简单粗暴!!!)
Java——使用集合实现简单的斗地主发牌功能(两种方式简单粗暴!!!)
中招了,重写TreeMap的比较器引发的问题...(上)
中招了,重写TreeMap的比较器引发的问题...(上)
中招了,重写TreeMap的比较器引发的问题...(上)

热门文章

最新文章