学习笔记------- == equals hashcode
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记------- == equals hashcode相关的知识,希望对你有一定的参考价值。
先来回顾一下 == 与equals 的区别
==是运算符
java中的数据类型,可分为两类:
1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean ,他们之间的比较,应该用双等号(==),比较的是他们的值。
2.引用数据类型,比如字符串,或者一个类的对象 当他们用(==)进行比较的时候,实际上比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。
equals是方法
JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址的(等同于“==”)
Object类中:
public boolean equals(Object obj) { return (this == obj); }
但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,比如String类中
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; }
因此,此时的equals不再是比较类在堆内存中的存放地址了。
对于引用类型数据之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的。
测试如下:
那么hashcode()是什么?
同equals(Object obj)一样,在Java中任何一个对象都具备hashcode()这个方法,因为他也是在Object类中定义的。 不同的是equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。
而hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。
关于这两个方法有一些必须要遵循的规范(最重要的两个):
规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。
规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。
总结如下:
1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。
2、如果两个对象不equals,他们的hashcode有可能相等。
3、如果两个对象hashcode相等,他们不一定equals。
4、如果两个对象hashcode不相等,他们一定不equals。
上面我们已经知道String类重写了equals方法,那它有没有重写hashcode方法呢?
Object中的hashcode方法:
public native int hashCode();
对,你没有看错object中并没有实现hashcode 他是一个native方法,只在联合开发中用到,我们只用知道底层会返回给我们对应的int值即可。
而在String类中
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
可以看到String已经将hashcode重写。测试一下
当然hashcode相当 不一定equals
从上可知,不同的对象,hashcode()可能相等的,这也就有了hash表的冲突解决问题,有了对象相等比较时的equals()问题了。
重写equals方法后为什么要重写hashcode方法,遵从这个规范会给我们带来哪些好处?
重写equals方法时需要重写hashCode方法,主要是针对Map、Set等集合类型的使用!
我们先来了解一下hashcode的作用。
HashCode有什么用?不妨举个例子:
1、假设内存中有0 1 2 3 4 5 6 7 8这8个位置,如果我有个字段叫做ID,那么我要把这个字段存放在以上8个位置之一,如果不用HashCode而任意存放,那么当查找时就需要到8个位置中去挨个查找
2、使用HashCode则效率会快很多,把ID的HashCode%8,然后把ID存放在取得余数的那个位置,然后每次查找该类的时候都可以通过ID的HashCode%8求余数直接找到存放的位置了
3、如果ID的HashCode%8算出来的位置上本身已经有数据了怎么办?这就取决于算法的实现了,比如ThreadLocal中的做法就是从算出来的位置向后查找第一个为空的位置,放置数据;HashMap的做法就是通过链式结构连起来。反正,只要保证放的时候和取的时候的算法一致就行了。
4、如果ID的HashCode%8相等怎么办(这种对应的是第三点说的链式结构的场景)?这时候就需要定义equals了。先通过HashCode%8来判断类在哪一个位置,再通过equals来在这个位置上寻找需要的类。对比两个类的时候也差不多,先通过HashCode比较,假如HashCode相等再判断equals。如果两个类的HashCode都不相同,那么这两个类必定是不同的。
举个实际的例子Set。我们知道Set里面的元素是不可以重复的,那么如何做到?Set是根据equals()方法来判断两个元素是否相等的。比方说Set里面已经有1000个元素了,那么第1001个元素进来的时候,最多可能调用1000次equals方法,如果equals方法写得复杂,对比的东西特别多,那么效率会大大降低。使用HashCode就不一样了,比方说HashSet,底层是基于HashMap实现的,先通过HashCode取一个模,这样一下子就固定到某个位置了,如果这个位置上没有元素,那么就可以肯定HashSet中必定没有和新添加的元素equals的元素,就可以直接存放了,都不需要比较;如果这个位置上有元素了,逐一比较,比较的时候先比较HashCode,HashCode都不同接下去都不用比了,肯定不一样,HashCode相等,再equals比较,没有相同的元素就存,有相同的元素就不存。如果原来的Set里面有相同的元素,只要HashCode的生成方式定义得好(不重复),不管Set里面原来有多少元素,只需要执行一次的equals就可以了。这样一来,实际调用equals方法的次数大大降低,提高了效率。
简单来说,一般用java中的Map对象进行存储时,他会先自动调用hashCode方法来比较两个对象是否相等,如果相等再继续根据equals方法判断是否为同一对象。
所以如果我们对equals方法进行了重写,建议一定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不同的对象返回不同的hash值。
举个hashset的小例子:
第一部分是new出来的两个对象,它全加了进去。
第二部分,对象是相同的只加一个。
第三部分是new出来的两个对象,但String重写了hashcode和equals方法,断定为相同。
第一部分中,Persion类是Object的子类继承了Object的equals和hashcode方法,而在Object类当中它的不同的实例中的hashcode是不同的,这是Object中本来就定义好了的hashcode;所以继承它的类也都有这样的方法,所以第一部分通过equals之后Persion的两个实例是不相同的
第一部分钟如果重写equals方法,不重写hashcode方法有什么结果呢?
加入我们定义名字相同的人就是同一个人,然后重写equals方法,不重写hashcode
public boolean equals(Object obj) { if(this==obj) { return true; } if(obj!=null&&obj instanceof Person) { Person p = (Person)obj; if(name.equals(p.name)) { return true; } } return false; }
测试
定义好名字相同即为一人,在加入到set里时却还是认为是两个不同的人。
所以重写equals方法时也要重写hashcode方法。
以上是关于学习笔记------- == equals hashcode的主要内容,如果未能解决你的问题,请参考以下文章
学习笔记------- == equals hashcode