标签:java语言 程序设计 学java shc ceo 内存 样本 复杂 ati
不积跬步,无以至千里;不积小流,无以成江海。
Java语言基础
Java的hashcode()和equals()方法
比较两个对象是否相等,它与 == 的比较有本质的不同,
在Java 体系中,系统把判断对象是否相等的权力交给程序员。具体的措施是把 equals() 方法写到 Object 类中,并让所有类继承 Object 类。 这样程序员就能在自定义的类中重写 equals()
方法, 从而实现自己的比较逻辑。
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。
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 方法,执行 user1.equals(user2) 比较的就是两个对象的地址(即 p1 == p2),肯定是不相等的,见 Object 源码:
public boolean equals(Object object){ if(this == object){ return true; } }
既然比较两个对象是否相等,使用的是 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
一起学Java(二十四)-----hashcode()和equals()
标签:java语言 程序设计 学java shc ceo 内存 样本 复杂 ati
原文地址:https://www.cnblogs.com/smilexuezi/p/12003176.html