一起学Java(二十四)-----hashcode()和equals()

不积跬步,无以至千里;不积小流,无以成江海。

Java语言基础

Java的hashcode()和equals()方法

  • equals()

比较两个对象是否相等,它与 == 的比较有本质的不同,

在Java 体系中,系统把判断对象是否相等的权力交给程序员。具体的措施是把 equals() 方法写到 Object 类中,并让所有类继承 Object 类。 这样程序员就能在自定义的类中重写 equals()

方法, 从而实现自己的比较逻辑。

  • hashCode()

hashCode() 的意思是哈希值,哈希函数能够保证相同的输入能够得到相同的输出(哈希值),但是不能够保证不同的输入总是能得出不同的输出。

当输入的样本量足够大时,是会产生哈希冲突的,也就是说不同的输入产生了相同的输出。

Java 的容器类被分为 Collection 和 Map 两大类,Collection 又可以进一步分为 List 和 Set。 其中 Map 和 Set 都是 不允许元素重复 的,严格来说Map存储的是键值对,它不允

许重复的键值。

值得注意的是:Map 和 Set 的绝大多数实现类的底层都会用到 散列表 结构。

上面提到 Set 和 Map 不存放重复的元素(key),这些容器在存储元素的时必须对元素做出判断:在当前的容器中有没有和新元素相同的元素?

难道不能直接调用元素对象的 equals() 方法进行比较?

如果容器中的存储的对象数量较少,确实可以,但是如果容器中存放的对象达到了一定的规模,就不是一件容易的事情了。

这时候再看看hashCode,在散列表的基础上,判断“新对象是否和已存在对象相同”就容易得多了。

由于每个对象都自带有 hashCode(),这个 hashCode 将会用作散列表哈希函数的输入,hashCode 经过哈希函数计算后得到哈希值,新对象会根据哈希值,存储到相应的内存的单元。

我们不妨假设两个相同的对象,hashCode() 一定相同

由于相同的输入一定会产生相同的输出,于是如果新对象,和容器中已存在的对象相同,新对象计算出的哈希值就会和已存在的对象的哈希值产生冲突。

这时容器就能判断:这个新加入的元素已经存在,需要另作处理:覆盖掉原来的元素(key)或舍弃。

所以,如果这个元素计算出的哈希值所对应的内存单元没有产生冲突,也就是没有重复的元素,那么它就可以直接插入。

当运用 hashCode() 时,判断是否有相同元素的代价,只是一次哈希计算,时间复杂度为O(1),这极大地提高了数据的存储性能。

注意:

前面我们还提到:当输入样本量足够大时,不相同的输入是会产生相同输出的,也就是形成哈希冲突。

这样,原来我们设定的“如果产生冲突,就意味着两个对象相同”的规则瞬间被打破,因为产生冲突的很有可能是两个不同的对象!

当然除了 hashCode() 方法,还有 equals() 方法。

也就是说当两个不相同的对象产生哈希冲突后,我们可以用 equals() 方法进一步判断两个对象是否相同。

这时 equals() 方法就相当重要了,这个情况下它必须要能判定这两个对象是不相同的。

 Java 程序设计中一个重要原则:

如果两个对象是相等的,它们的 equals() 方法应该要返回 true,它们的 hashCode() 需要返回相同的结果

两个规范:

1. 若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。

2. 如果equals(Object obj)返回false,即两个对象"不相同",并不要求对这两个对象调用hashcode()方法得到两个不相同的数。

推论:

1、如果两个对象equals,Java虚拟机会认为他们的hashcode一定相等。

2、如果两个对象不equals,他们的hashcode有可能相等。

3、如果两个对象hashcode相等,他们不一定equals。

4、如果两个对象hashcode不相等,他们一定不equal。

  • 重写对象的 equals 方法和 hashCode 方法

Java 对象如果要比较是否相等,则需要重写 equals 方法,同时重写 hashCode 方法

package Allmethod;

class Person{
	
	private int age;
	private String name;
	
	public Person(){
		
	}
	
	public Person(int age, String name) {
		this.age = age;
		this.name = name;
	}
	
	public int getAge() {
		return age;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public boolean equals(Object object){
		if(this == object){
			return true;
		}
		if(object == null){
			return false;//非空性:对于任何非空引用x.equals(null)为false
		}
		if(object instanceof Person){
			Person other = (Person) object;
			//需要比较的字段相等,则两个对象相等,返回true
			return other.getAge() == this.getAge() && other.getName() == this.getName();		
		}
		return false;
	}
	
	public int hashCode(){
		int result = 17;
		result = 31 * result + (name == null ? 0 : name.hashCode());
		result = 31 * result + age;
		return result;
	}
	
	
}


public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Person p = new Person(24, "tutu");
		Person p1 = new Person(25, "tutu");
		Person p2 = new Person(25, "tutu");
		System.out.println(p.equals(p1));
		System.out.println(p1.equals(p2));
		System.out.println(p.hashCode());
		System.out.println(p1.hashCode());
		System.out.println(p2.hashCode());

	}

}

程序输出:

false
true
110745447
110745448
110745448

  

  • 为什么要重写 equals 方法

因为不重写 equals 方法,执行 user1.equals(user2) 比较的就是两个对象的地址(即 p1 == p2),肯定是不相等的,见 Object 源码:

public boolean equals(Object object){
	if(this == object){
		return true;
	}
}

  

  • 为什么要重写 hashCode 方法

既然比较两个对象是否相等,使用的是 equals 方法,那么只要重写了 equals 方法就好了,干嘛又要重写 hashCode 方法呢?

其实当 equals 方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。当我们删掉重写的 hashCode 方法。

程序输出:

false
true
366712642
1829164700
2018699554

显然,这不是我们要的结果,我们是希望两个对象如果相等,其 hashCode 值也应该相等。

 博客借鉴:https://www.cnblogs.com/yuxiaole/p/9570850.html

               https://www.cnblogs.com/tanshaoshenghao/p/10915055.html

猜你喜欢

转载自www.cnblogs.com/smilexuezi/p/12003176.html