讲解:为什么重写equals时必须重写hashCode方法

时间:2022-04-13 16:02:27

一 :string类型的==和equals的区别:

        结论:"=="是判断两个字符串的内存地址是否相等,equals是比较两个字符串的值是否相等,具体就不做扩展了,有兴趣的同学可以去查看相关的博客。

        String s1 = new String("java");

       String s2 = new String("java");

       System.out.println(s1==s2); //false

       System.out.println(s1.equals(s2)); //true

       在Java中,equals和hashCode方法是Object中提供的两个方法,string类型重写了equals()方法和hashCod()方法,源码如下:

public boolean equals(Object anObject) {
	    if (this == anObject) {
	        return true;
	    }
	    if (anObject instanceof String) {
	        String anotherString = (String)anObject;
	        int n = count;
	        if (n == anotherString.count) {
	        char v1[] = value;
	        char v2[] = anotherString.value;
	        int i = offset;
	        int j = anotherString.offset;
	        while (n-- != 0) {
	            if (v1[i++] != v2[j++])
	            return false;
	        }
	        return true;
	        }
	    }
	    return false;
	    }

	/*返回哈希码,String的哈希码计算方式为s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]*/
	public int hashCode() {
	    int h = hash;
	    if (h == 0) {
	        int off = offset;
	        char val[] = value;
	        int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
	            hash = h;
        }
	        return h;
    }

  

 二:对象equals 和hashcode的关系

结论:当对象的equals()方法被重写时,通常有必要重写 hashCode() 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:

          (1)两个对象相等,hashcode一定相等

          (2)两个对象不等,hashcode不一定不等

          (3)hashcode相等,两个对象不一定相等

          (4)hashcode不等,两个对象一定不等

          hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但 不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致。

 

三 :使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()

    A.当put元素时:
              1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
              2.如果该下标位置有元素(即根据put元素的key算出的hashcode一样即重复了),则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中。这里由于hashcode一样,所以得出的数组下标位置相同。所以会在该  数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。       
        B.当get元素时:
             根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置一个链表,则需要调用equals方法遍历链表中的所有元素与当前的元素比较,得到真正想要的对象。
        

       C. 采用hashcode的好处:

          如果我们不使用hashcode,直接使用equals比较,那么我们有10w个元素,put值或者get值时要比较10w次,如果先通过hashcode判断后,我们的效率就会提高很多。

          我们可以对比实际生活中,如果有一栋教学楼,一楼是1年级,二楼是2年级,以此类推,六楼是6年级,这时我们需要找5年级2班在哪间教室,通过hashcode直接定位到5楼,然后在5楼一间间找就行了,这样避免从一楼一间一间查看。

       

 四.:代码展示,为什么重写equals时必须重写hashCode方法

         我们以一个student 学生类来测试重写equals()和重写hashcode()后,map的get()方法返回值结果,让大家清晰了解为什么重写equals时必须重写hashCode方法

          

package com.sinaif;

public class Student {
	private String name; //姓名
	private String sex;  //性别
	private String idCard;//身份证号
	Student(String name,String sex,String idCard){
		this.name = name;
		this.sex = sex;
		this.idCard = idCard;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getIdCard() {
		return idCard;
	}
	public void setIdCard(String idCard) {
		this.idCard = idCard;
	}
	
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(obj == null){
			return false;
		}
		Student obj1 = (Student)obj;
		if(this.name.equals(obj1.getName()) && this.idCard.equals(obj1.getIdCard())){
			return true;
		}
		return false;
	}
	
	//@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		String s= this.name + this.idCard;
		return s.hashCode();
	}
	
}

 

 

public static void main(String[] args) throws Exception {
		Map<Student,Student> map = new HashMap<Student,Student>();
		Student s1 = new Student("haly","male","110200");
		Student s2 = new Student("haly","female","110200");
		map.put(s1, s1);
  
		System.out.println(map.get(s2)); 
		
		// 当没有重写equals方法和hashcode方法,打印出null
		// 当重写equals方法,不重写hashcode方法时,打印出null
		// 当重写equals方法,并且重写hashcode方法时,打印出com.test.Student@95fb7d6
		
	}