正确使用    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)));
  
输出为    99
、    88
、    77
,一切正常。  
把    HashMap
改为    TreeMap
再测试:  
Map<Student, Integer> map = new TreeMap<>();
输出为    null
、    null
、    null
!  
说好的接口不变,实现类随便换现在不管用了?难道是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);
}
  修正代码后,再次运行,一切正常。