粉丝答疑:为什么我写的treeMap.get(key)获取的值为null?

您所在的位置:网站首页 华为fusionstorage是什么 粉丝答疑:为什么我写的treeMap.get(key)获取的值为null?

粉丝答疑:为什么我写的treeMap.get(key)获取的值为null?

2023-04-03 08:55| 来源: 网络整理| 查看: 265

image.png

昨天粉丝在后台问了我一个问题,他写了一个TreeMap,但是通过key获取的值是null,百思不得其姐😔,也百思不得其寐,让我们来一看究竟。

📌问题复现

先定义一个Users对象,里面实现了Comparable接口

@Data @AllArgsConstructor public class Users implements Comparable { private String name; private int age; @Override public int compareTo(Users users) { // 定义比较规则,正数-大,负数-小,0-相等 if (this.age > 0) { return 1; } if (this.age == 0) { //年龄相等再比较名字 return this.name.compareTo(users.getName()); } return -1; } }

写一个TreeMap测试类。

/** * the TestTree * @author Java实用技术手册 * @date 2022-07-26 */ public class TestTree { public static void main(String[] args) { // 实例化TreeMap, 使用Users, 类型来比较外部比较器.红黑树结构 Map map = new TreeMap(); Users u1 = new Users("Tom", 19); Users u2 = new Users("Jack", 20); map.put(u1, "Tom"); map.put(u2, "Jack"); Set keys = map.keySet(); for (Users key : keys) { System.out.println(key + "----" + map.get(key)); } } }

运行结果 ![[java6.png]]

🎯原因 TreeMap基础知识

我们知道TreeMap是一个有序key-value集合,它的排序是通过key的自然排序或者指定的比较器实现的。底层数据结构是红黑树。

用官方的说法是: TreeMap 有两种排序:

自然排序: TreeMap 的所有Key必须实现 Comparable 接口,而且所有的key应该是同一个类的对象,否则将会抛出 ClassCastException 异常。 定制排序: 创建TreeMap 时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。采用定制排序时不要求Map的key实现 Comparable接口。

用人话就是:

自然排序:key本身可以按照abcd字母排序,或者key本身实现 Comparable 接口,通过 k1.compareTo(k2)能确定谁先谁后的规则也行。 定制排序:自己写一个比较器Comparator,其实也是为了通过 k1.compareTo(k2)能确定谁先谁后。 数据存储

![[hongheishu 1.gif]] 这里我推荐一个美国旧金山大学的数据结构可视化网页。https://www.cs.usfca.edu/~galles/visualization/Algorithms.html 你可以通过手动添加数据,查看数据结构中的变化过程,比如上述红黑树,你可以通过页面看到根和叶子变化情况,从而加深理解。

源码探索 // java.util.TreeMap public V get(Object key) { Entry p = getEntry(key); return (p==null ? null : p.value); } final Entry getEntry(Object key) { // Offload comparator-based version for sake of performance if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); // 核心代码在这里,通过自定义的比较器,从根节点开始比较查找节点 @SuppressWarnings("unchecked") Comparable k = (Comparable) key; Entry p = root; while (p != null) { // 根据比较器结果,决定继续往左还是往右查找 int cmp = k.compareTo(p.key); if (cmp 0) p = p.right; // K大于P,继续往右查找 else return p; // 终于查找到了 } return null; // 查找到叶子边缘还是找不到就算了吧 }

该同学写的比较器,只根据age判断,任何一个age>0的users进来,都会返回1,也就是会让TreeMap继续往右查找,最后就是查到20右边的NULL🍀。 这个同学的想法是对的,但是写法不对,比较器,应该跟传入的对象比较,而不是跟自己比较。

@Override public int compareTo(Users users) { // 定义比较规则,正数-大,负数-小,0-相等 if (this.age > 0) { return 1; } if (this.age == 0) { return this.name.compareTo(users.getName()); } return -1; } 修改

知道了上面的问题,修改也很简单,就是修改下比较器。跟传入的users.age比较,如果age都相等了,再比较name。

@Override public int compareTo(Users users) { // 定义比较规则,正数-大,负数-小,0-相等 if (this.age > users.getAge()) { return 1; } if (this.age == users.getAge()) { return this.name.compareTo(users.getName()); } return -1; }

修改后运行结果:

Users(name=Tom, age=19)----Tom Users(name=Jack, age=20)----Jack 📝总结

使用TreeMap时,一定要记住key需要有一个稳定的比较器!什么是稳定?就是 不论A、B的传入顺序有什么变化,两次比较的结果都一样,即 A > B === B < A。

今天的内容就到这里,希望对你以后开发有帮助。

我是Pandas,专注Java编程实用技术分享,公众号「Java实用技术手册」和B站均有视频解说,欢迎来玩。 如果你觉得这篇文章有用,别忘了点赞+关注,一起进步!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3