HashMap什么时候重写hashcode和equals方法,为什么需要重写

HashSet内部是通过HashMap实现。只有使用排序的时候才使用TreeMap。否知使用HashMap。

使用HashSet 举例子:  情景描述  往HashSet集合中put Student对象实例,要求: code name相同的不重复 放入

HashSet  set = new HashSet

set.add(new Student("1", "aa"));

set.add(new Student("2", "aa"));

set.add(new Student("1", "aa"));

System.out.println("set:===" + set);


结果set集合内的元素为3个,没有去处重复的new Student("1","aa") ?为什么呢?
这里由于两个new Student("1","aa")是不一样的Student对象实例(都是new 出来的,对象引用不一样)。而默认的Student类的hashcode是根据对象的引用算的。所以直接认为是两个不一样的对象,直接add进去了。所以需要重写hashcode方法如果hashcode不一样则直接认为是不同对象,如下:

@Data
class Student {
    String code;
    String name;
   
    @Override
    public int hashCode() {
        return code.hashCode() + name.hashCode();
    }
    public Student(String code, String name) {
        this.code = code;
        this.name = name;
    }
}

结果发现还是不对,还是add进去了3个Student  呢?
这里重写的hashcode是一样的,所以还是add进去了。所以还需要重新equals方法。

其实是有这样一个规定,如果hashcode一样时,则还需要继续调用equals方式看看对象是否相等。所以再需要重写 equals方法如下即可实现:

  @Override
    public boolean equals(Object o) {
        Student s = (Student) o;
        if (name.equals(s.getName()) && code.equals(s.getCode())) {
            return true;
        }
        return false;
    }

完整test demo:

public class MyClass {
    public static void main(String[] args) {
        HashSet set = new HashSet<>();
        set.add(new Student("1", "aa"));
        set.add(new Student("2", "aa"));
        set.add(new Student("1", "aa"));
        System.out.println("set:===" + set);

    }
}

@Data
class Student {
    String code;
    String name;

    @Override
    public int hashCode() {
        return code.hashCode() + name.hashCode();
    }
    public Student(String code, String name) {
        this.code = code;
        this.name = name;
    }
}

可以看到如果hashcode不一样就直接认为是不一样的对象,不需要再去equal比较,更加节省时间。
如果new Student("1","aa")、new Student("1","bb")。通过code和name算出的hashcode就可以算是不一样的对象,就不需要再去equals比较。
往往HashSet中存放的对象是否相等的逻辑都需要自己定义,而并不会直接用默认的引用来算,即一般都会重新hashcode和equals方法,而且同时需要重写。以后要注意哦。

HashMap的put和get也类似。
HashMap是底层实现时数组加链表。
       A.当put元素时:
              1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
              2.如果该下标位置有元素(即根据put元素的key算出的hashcode一样即重复了),则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中。这里由于hashcode一样,所以得出的数组下标位置相同。所以会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。       
        B.当get元素时:
             根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置一个链表,则需要调用equals方法遍历链表中的所有元素与当前的元素比较,得到真正想要的对象。
可以看出如果根据hashcdoe算出的数组位置尽量的均匀分布,则可以避免遍历链表的情况,以提高性能。
所以要求重写hashmap时,也要重写equals方法。以保证他们是相同的比较逻辑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章