hashCode() 和 equals() 总结

一、问题

       hashCode() 方法和 equal() 方法的作用其实一样,在 Java 里都是用来对比两个对象是否相等一致,那么 equal() 既然已经能实现对比的功能了,为什么还要 hashCode() 呢?

二、答案

       因为重写的 equal() 里一般比较的比较全面比较复杂,这样效率就比较低,而利用 hashCode() 进行对比,则只要生成一个 hash 值进行比较就可以了,效率很高,那么 hashCode() 既然效率这么高为什么还要 equal() 呢?

       因为 hashCode() 并不是完全可靠,有时候同的对象他们生成的 hashcode 也会一样(生成 hash 值得公式可能存在的问题),所以 hashCode() 只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出结论:   

        1、equal() 相等的两个对象他们的 hashCode() 肯定相等,也就是用 equal() 对比是绝对可靠的。

        2、hashCode() 相等的两个对象他们的 equal() 不一定相等,也就是 hashCode() 不是绝对可靠的。

三、总结

       所以,对于需要大量并且快速的对比的话如果都用 equal() 去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用 hashCode() 去对比,如果 hashCode() 不一样,则表示这两个对象肯定不相等(也就是不必再用 equal() 去再对比了),如果 hashCode() 相同,此时再用 equal() 对比,若 equal() 也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性! 

       这种大量的并且快速的对象对比一般在 hash 容器中使用,比如 hashset、hashmaphashtable 等等,比如 hashset 里要求对象不能重复,则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先 hashCode(),如果 hashCode() 相同,再用 equal() 验证,如果 hashCode() 都不同,则肯定不同,这样对比的效率就很高了。

      hashCode() equal() 都是基本类 Object 类里的方法,而和 equal() 一样, Object hashCode() 里面只是返回当前对象的地址,如果是这样的话,那么我们相同的一个类,new 两个对象,由于他们在内存里的地址不同,则他们的 hashCode() 不同,所以这显然不是我们想要的,所以我们必须重写我们类的 hashCode() 方法,即一个类,在 hashCode() 里面返回唯一的一个 hash 值。

四、举例说明

       自定义一个类 Person,由于标识这个类的是他的内部的变量 num 和 name,所以我们就根据他们返回一个 has h值,作为这个类的唯一 hash 值。

      所以如果我们的对象要想放进 hashSet,并且发挥 hashSet 的特性(即不包含一样的对象),则我们就要重写我们类的 hashCode() 和 equal() 方法了。像 String,Integer 等这种类内部都已经重写了这两个方法。

class Person{
 
    private int num;

    private String name;
 
    public int hashCode(){
        return num*name.hashCode();
    }
 

        当然如果我们只是平时想对比两个对象是否一致,而不是加入hashSet里面,那么只需要重写一个 equal(),然后利用 equal() 去对比也行的。

五、哈希码(HashCode) 

5.1 概要

       首先我们知道哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但不表示不同的对象哈希码完全不同也有相同的情况,具体要看程序员是如何写哈希码的算法。

5.2 什么是 HashCode

       在 Java 中,哈希码代表对象的特征,例如下面的例子,根据 HashCode 由此可得出 str1 != str2str1 == str3

String str1 = “aa”, str1.hashCode= 3104

String str2 = “bb”, str2.hashCode= 3106

String str3 = “aa”, str3.hashCode= 3104

5.3 常用哈希算法

        1、Object 类的 hashCode() 返回对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。

       2、String 类的 hashCode() 根据 String 类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串所在的堆空间相同,返回的哈希码也相同。

       3、Integer 类,返回的哈希码就是 Integer 对象里所包含的那个整数的数值,例如 Integer i1=new Integer(100), i1.hashCode 的值就是 100 。由此可见,2 个一样大小的 Integer 对象,返回的哈希码也一样。

六、equals方法

6.1 作用

       1、默认情况(没有覆盖 equals 方法)下 equals 方法都是调用 Object 类的 equals 方法,而 Object equals 方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。

       2、要是类中覆盖了 equals 方法,那么就要根据具体的代码来确定 equals 方法的作用了,覆盖后一般都是通过对象的内容是否相等来判断对象是否相等。

6.2 举例

        没有覆盖 equals 方法代码如下:

// 学生类  
public class Student {  
	private int age;  
	private String name;  
	  
	public Student() {  
	}  
	public Student(int age, String name) {  
		super();  
		this.age = age;  
		this.name = name;  
	}  
	// setter、getter
}

        测试代码如下:

public class EqualsTest {  
    public static void main(String[] args) {  
        LinkedList<Student> list = new LinkedList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(3,"张三");  
        Student stu2  = new Student(3,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  
          
        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());  
    }  
  
}
// stu1 == stu2 : false
// stu1.equals(stu2) : false
// list size:2
// set size:2

       Student 类没有覆盖 equals 方法,stu1 调用 equals 方法实际上调用的是 Object 的 equals 方法。所以采用对象内存地址是否相等来判断对象是否相等。因为是两个新对象所以对象的内存地址不相等,所以 stu1.equals(stu2) 是 false

        我们覆盖一下 equals 方法( age 和 name 属性),让 Student 类其通过判断对象的内容是否相等来确定对象是否相等。覆盖后的 Student 类如下:

public class Student {  
    private int age;  
    private String name;
	// setter、getter
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	} 
}
// stu1 == stu2 : false
// stu1.equals(stu2) : true
// list size:2
// set size:2

        因为 Student 两个对象的 age 和 name 属性相等,而且又是通过覆盖 equals 方法来判断的,所示 stu1.equals(stu2)  true。注意以上几次测试 list set size 都是 2

6.3 HashCode 出场

       通过以上的代码运行,我们知道 equals 方法已经生效。接下来我们在覆盖一下 hashCode 方法(通过 age 和 name 属性来生成 hashcode)并不覆盖 equals 方法,其中 Hash 码是通过 age name 生成的。

        覆盖 hashcode 后的 Student 类:

     //学生类  
    public class Student {  
        private int age;  
        private String name;  
          
        public Student() {  
        }  
        public Student(int age, String name) {  
            super();  
            this.age = age;  
            this.name = name;  
        }  
        // setter、getter
        @Override  
        public int hashCode() {  
            final int prime = 31;  
            int result = 1;  
            result = prime * result + age;  
            result = prime * result + ((name == null) ? 0 : name.hashCode());  
            return result;  
        }     
    }

        测试代码如下:

public class HashCodeTest {  
    public static void main(String[] args) {  
        LinkedList<Student> list = new LinkedList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(3,"张三");  
        Student stu2  = new Student(3,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  
        System.out.println("hashCode:"+stu1.hashCode());
        System.out.println("hashCode:"+stu2.hashCode());
        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());  
    }  
}
// stu1 == stu2 : false
// stu1.equals(stu2) : false
// list size:2
// hashCode:775943
// hashCode:775943
// set size:2

       结果分析:我们并没有覆盖 equals 方法只覆盖了 hashCode 方法,两个对象虽然 hashCode 一样,但在将 stu1 stu2 放入 set 集合时由于 equals 方法比较的两个对象是 false,所以就没有再比较两个对象的 hashcode 值。 

        接下来我们覆盖一下 equals 方法和 hashCode 方法

//学生类  
public class Student {  
	private int age;  
	private String name;  

	public Student() {  
	}  
	public Student(int age, String name) {  
		super();  
		this.age = age;  
		this.name = name;  
	}  
	// setter、getter
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}     
}

        测试代码如下:

public class HashCodeTest {  
    public static void main(String[] args) {  
        LinkedList<Student> list = new LinkedList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(3,"张三");  
        Student stu2  = new Student(3,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  
        System.out.println("hashCode:"+stu1.hashCode());
        System.out.println("hashCode:"+stu2.hashCode());
        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());  
    }  
}
// stu1 == stu2 : false
// stu1.equals(stu2) : true
// list size:2
// hashCode:775943
// hashCode:775943
// set size:1

        stu1 stu2 通过 equals 方法比较相等,而且返回的 hashCode 值一样,所以放入 set 集合中时只放入了一个对象。

        下面我们让两个对象 equals 方法比较相等,但 hashCode 值不相等试试。 

 public class Student {  
	private int age;  
	private String name;  
	private static int index=5;
	public Student() {  
	}  
	public Student(int age, String name) {  
		super();  
		this.age = age;  
		this.name = name;  
	}  
	// setter、getter
	@Override
	public int hashCode() {
		final int prime = 31;  
		int result = 1;  
		result = prime * result + (age+index++);  
		result = prime * result + ((name == null) ? 0 : name.hashCode());  
		return result; 
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}     

}

        测试代码如下:

public class HashCodeTest {  
    public static void main(String[] args) {  
        LinkedList<Student> list = new LinkedList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(3,"张三");  
        Student stu2  = new Student(3,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  
        System.out.println("hashCode:"+stu1.hashCode());
        System.out.println("hashCode:"+stu2.hashCode());
        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());  
    }  
}
// stu1 == stu2 : false
// stu1.equals(stu2) : true
// list size:2
// hashCode:776098
// hashCode:776129
// set size:2

        虽然 stu1 stu2 通过 equals 方法比较相等,但两个对象的 hashcode 的值并不相等,所以在将 stu1 stu2 放入 set 集合中时认为是两个不同的对象。

6.4 修改对象属性值

package com;

//学生类  
public class Student {  
	private int age;  
	private String name;  

	public Student() {  
	}  
	public Student(int age, String name) {  
		super();  
		this.age = age;  
		this.name = name;  
	}  
    // setter、getter
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}     

}

        测试代码如下:

public class HashCodeTest {  
    public static void main(String[] args) {  
        LinkedList<Student> list = new LinkedList<Student>();  
        Set<Student> set = new HashSet<Student>();  
        Student stu1  = new Student(3,"张三");  
        Student stu2  = new Student(3,"张三");  
        System.out.println("stu1 == stu2 : "+(stu1 == stu2));  
        System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));  
        list.add(stu1);  
        list.add(stu2);  
        System.out.println("list size:"+ list.size());  
        set.add(stu1);  
        set.add(stu2);  
        System.out.println("set size:"+ set.size());
        stu1.setAge(34);  
        System.out.println("remove stu1 : "+set.remove(stu1));  
        System.out.println("set size:"+ set.size()); 
    }  
}
// stu1 == stu2 : false
// stu1.equals(stu2) : true
// list size:2
// set size:1
// remove stu1 : false
// set size:1

        当我们将某个对象存到 set 中时,如果该对象的属性参与了 hashcode 的计算,那么以后就不能修改该对象参与 hashcode 计算的那些属性了,否则会引起意向不到的错误的。正如测试中,不能够移除 stu1 对象。 

七、总结

        1、equals 方法用于比较对象的内容是否相等(覆盖以后)

        2、hashcode 方法只有在集合中用到

        3、当覆盖了 equals 方法时,比较对象是否相等将通过覆盖后的 equals 方法进行比较(判断对象的内容是否相等)。

        4、将对象放入到集合中时,首先判断要放入对象的 hashcode 值与集合中的任意一个元素的 hashcode 值是否相等,如果不相等直接将该对象放入集合中。如果 hashcode 值相等,然后再通过 equals 方法判断要放入对象与集合中的任意一个对象是否相等,如果 equals 判断不相等,直接将该元素放入到集合中,否则不放入。

        5、将元素放入集合的流程图

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值