原文:https://www.liaoxuefeng.com/article/1256136507802816

正确使用Map,只需要正确实现hashCode()equals()就行了吗?

恐怕还不行。

确切地说,如果使用的是HashMap,那么只需要正确实现hashCode()equals()就够了。

但是,如果换成TreeMap,正确实现hashCode()equals(),结果并不一定正确。

代码胜于雄辩。先看作为key的class定义:

class Student implements Comparable<Student> {
final String name;
final int score; public Student(String name, int score) {
this.name = name;
this.score = score;
} @Override
public int hashCode() {
return Objects.hash(name, score);
} @Override
public boolean equals(Object obj) {
if (obj instanceof Student) {
Student o = (Student) obj;
return Objects.equals(this.name, o.name) && this.score == o.score;
}
return false;
} @Override
public int compareTo(Student o) {
return this.score < o.score ? -1 : 1;
}
}

先用HashMap测试:

Map<Student, Integer> map = new HashMap<>();
map.put(new Student("Michael", 99), 99);
map.put(new Student("Bob", 88), 88);
map.put(new Student("Alice", 77), 77);
System.out.println(map.get(new Student("Michael", 99)));
System.out.println(map.get(new Student("Bob", 88)));
System.out.println(map.get(new Student("Alice", 77)));

输出为998877,一切正常。

HashMap改为TreeMap再测试:

Map<Student, Integer> map = new TreeMap<>();

输出为nullnullnull

怎么肥四?

说好的接口不变,实现类随便换现在不管用了?难道是JDK的bug?

遇到这种诡异的问题,首先在心里默念三遍:

  • JDK没有bug。
  • JDK没有bug。
  • JDK没有bug。

然后开始从自己的代码找原因。

先打开JDK的TreeMap文档,注意到这句话:

This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method

意思是,Map接口定义了使用equals()判定key是否相等,但是SortedMap却使用compareTo()来判断key是否相等,而TreeMap是一种SortedMap

所以,问题出在compareTo()方法上:

@Override
public int compareTo(Student o) {
return this.score < o.score ? -1 : 1;
}

上面这个定义,用来排序是没问题的,但是,没法判断相等。TreeMap根据key.compareTo(anther)==0判断是否相等,而不是equals()

所以,解决问题的关键是正确实现compareTo(),该相等的时候,必须返回0

@Override
public int compareTo(Student o) {
int n = Integer.compare(this.score, o.score);
return n != 0 ? n : this.name.compareTo(o.name);
}

修正代码后,再次运行,一切正常。

最新文章

  1. Jquery学习笔记--性能优化建议
  2. android 全屏视频播放(SurfaceView + MediaPlayer)
  3. [开源应用]利用HTTPHandler+resumableJs+HTML5实现拖拽上传[大]文件
  4. PLSQL Package dubug方法
  5. 基于visual Studio2013解决C语言竞赛题之1040因数分解
  6. Windows 8 动手实验系列教程 实验5:进程生命周期管理
  7. HTML入门
  8. 树莓派小车By 树莓派爱好者ITJoker(通过C socket通信实现树莓派视频小车)(二)
  9. 110个oracle常用函数总结
  10. 基于Python的Webservice开发(二)-如何用Spyne开发Webservice
  11. JVM基础系列第4讲:从源代码到机器码,发生了什么?
  12. 微软拼音转换工具类ChnCharInfo.dll
  13. day-03(js)
  14. Eloquent JavaScript #05# higher-order functions
  15. 20165313 《Java程序设计》第八周学习总结
  16. 用IrisSkin2.dll美化你的WinForm
  17. (转)WCF中神秘的“8733&quot;端口和“Design_Time_Addresses”
  18. 【转】Java学习&mdash;什么是时间复杂度
  19. 20155333 2016-2017-2 《Java程序设计》第五周学习总结
  20. 替换国内yum源以及pip源

热门文章

  1. tomcat关闭异常导致的项目无法重启
  2. LeetCode Top Interview Questions
  3. Tomcat免安装版执行tomcat9.exe闪退
  4. SOC中的DMIPS_GFLOPS_GMACS的含义
  5. JavaScript有用的代码片段和trick
  6. POJ 1661 Help Jimmy(C)动态规划
  7. 有关java中的try{}catch(){}的讲解
  8. Linux 环境安装 Node、nginx、docker、vsftpd、gitlab
  9. ArcGIS Server SOE地图服务重启特别卡
  10. Oracle中的dual表简介