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

正确使用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);
}

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


compare源码:

    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

compareTo源码:

    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

来源于:

https://www.liaoxuefeng.com/article/001545657362937e0b6ff640a45422faec234c3f1f23f43000

猜你喜欢

转载自blog.csdn.net/weixin_41888813/article/details/85257255