欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-22 18:12:28
...

一 :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方法时,打印出[email protected]
		
	}