为什么要在重写了equals方法时还要重写hashcode方法
2014-08-19 21:48
525 查看
上篇博客中遗留了““重写了Object的equals方法,当所引用的对象是同一类对象且属性内容相等时,它们却不一定是相同的对象,这是为什么?我们又该如何解决”或则个问题?
也就是说在我们的程序中如使用了hashmap时,要比较两个对象是否相等,hashmap是通过key的hashcoe来查找,所以即使我们重写了equals方法,但是如果没有重写hashcode方法,得到的两个对象仍然不相等。那么,对于一些其他的类,即使我们不重写hashcode方法,编译器也不会报错,也不会影响到我们的使用。
在jdk中“注意:当此方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规协定,该协定声明相等对象必须具有相等的哈希码。”这样描述,所以我觉得还是在重写equals时,重写hashcode为好。
问题二:什么是hashCode方法?
hashCode的方法: publicnative int hashCode();
作用:
支持此方法是为了提高哈希表(例如
解析:
如果原来集合中已经有10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,这个效率可想而知。
然而,有了hashcode,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,那么就用equals比较,如果没有则直接插入,这样就大大减少了equals的使用次数,执行效率就大大提高了。
在测试类test中,hashmap中的key是用上面的key类new出来的一个对象,然后把这个对象作为hashmap的key,这里突出了hashmap的查找原理,hashmap是通过key类的hashcode方法来找到hashmap中的key(可以参考Hashmap中hashCode源码理解)。在测试类中,hashmap中放的是一个key类的对象引用,去get或者containsKey也是是这个对象引用,所以第一处、第二处、第三处的key是完全相同的,都是指向同一个对象,所以结果如下:
HashMap中的hashCode方法:
在这里hashmap中的key放的不是应用,而是new出来的对象,然后get或者containsKey的时候也通过new一个key类的对象,虽然我们初始化的内容完全相同,都是放int 1进去,也就是说对象内容完全相同,但是他们却不是指向同一个对象,所以最后输出的结果是:
既然重写了equals方法,那么比较的就是两个对象的内容,而不是地址,那么我们看一下运行结果:
显然,针对哈希表的这种集合光重写equals方法还是不行的,并不能说我们规定内容相同对象就可以相同,我们还必须重写hashcode方法。
这里我们重写了hashcode这个方法,让它返回一个我们初始化进去的i,这样你每次new一个对象,因为是通过hashcode找key,而你的hashcode有只是值i,所以只要i相等,你就可以找到你的key的地址,然后再用equals方法比较两个对象是不是相同,即开始比较我们传来的寻找value的key和hashmap中的key是不是同一个key,如果是那就找到了value。
运行结果:
达到了我们想要的运行效果。
问题一:我们先来看为什么会出现这样的问题?
首先,出现这个问题的根源在于哈希表的集合类型上。也就是说在我们的程序中如使用了hashmap时,要比较两个对象是否相等,hashmap是通过key的hashcoe来查找,所以即使我们重写了equals方法,但是如果没有重写hashcode方法,得到的两个对象仍然不相等。那么,对于一些其他的类,即使我们不重写hashcode方法,编译器也不会报错,也不会影响到我们的使用。
在jdk中“注意:当此方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规协定,该协定声明相等对象必须具有相等的哈希码。”这样描述,所以我觉得还是在重写equals时,重写hashcode为好。
问题二:什么是hashCode方法?
hashCode的方法: publicnative int hashCode();作用:
支持此方法是为了提高哈希表(例如
java.util.Hashtable提供的哈希表)的性能。
解析:
如果原来集合中已经有10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,这个效率可想而知。
然而,有了hashcode,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,那么就用equals比较,如果没有则直接插入,这样就大大减少了equals的使用次数,执行效率就大大提高了。
问题三:以实例解析为什么重写equals时需要重写hashcode?
1、 创建一个key类,作为hashMap的key
class key { int i; public key(int i){ this.i = i; } }
2、 创建一个value类,作为hashMap的value
class value { int j; public value(int j){ this.j =j; } public String toString(){ return " " + j; } }
3、 测试类:
public class test { public static void main(String args[]){ HashMap hm = new HashMap(); key k = new key(1); //第一处 value v = new value(2); hm.put(k,v); if (hm.containsKey(k))//第二处 { System.out.println(hm.get(k));//第三处 }else{ System.out.println("没有这样一个键值"); } } }
在测试类test中,hashmap中的key是用上面的key类new出来的一个对象,然后把这个对象作为hashmap的key,这里突出了hashmap的查找原理,hashmap是通过key类的hashcode方法来找到hashmap中的key(可以参考Hashmap中hashCode源码理解)。在测试类中,hashmap中放的是一个key类的对象引用,去get或者containsKey也是是这个对象引用,所以第一处、第二处、第三处的key是完全相同的,都是指向同一个对象,所以结果如下:
HashMap中的hashCode方法:
public int hashCode() { int h = 0; Iterator<Entry<K,V>> i =entrySet().iterator(); while (i.hasNext()) h += i.next().hashCode(); return h; }
4、 现在修改测试类
public class test { public static void main(String args[]){ HashMap hm = new HashMap(); hm.put(new key(1),new value(2)); if (hm.containsKey(new key(1))) { System.out.println(hm.get(new key(1))); }else{ System.out.println("没有这样一个键值"); } } }
在这里hashmap中的key放的不是应用,而是new出来的对象,然后get或者containsKey的时候也通过new一个key类的对象,虽然我们初始化的内容完全相同,都是放int 1进去,也就是说对象内容完全相同,但是他们却不是指向同一个对象,所以最后输出的结果是:
5、 重写key类的equals方法
class key { int i; public key(int i){ this.i = i; } public boolean equals(Object obj){ if (obj instanceof key) { if (((key)obj).i == i) { return true; } } return false; } }
既然重写了equals方法,那么比较的就是两个对象的内容,而不是地址,那么我们看一下运行结果:
显然,针对哈希表的这种集合光重写equals方法还是不行的,并不能说我们规定内容相同对象就可以相同,我们还必须重写hashcode方法。
6、 重写key类的hashCode方法
class key { int i; public key(int i){ this.i = i; } public boolean equals(Object obj){ if (obj instanceof key) { if (((key)obj).i == i) { return true; } } return false; } public int hashCode(){ return i; } }
这里我们重写了hashcode这个方法,让它返回一个我们初始化进去的i,这样你每次new一个对象,因为是通过hashcode找key,而你的hashcode有只是值i,所以只要i相等,你就可以找到你的key的地址,然后再用equals方法比较两个对象是不是相同,即开始比较我们传来的寻找value的key和hashmap中的key是不是同一个key,如果是那就找到了value。
运行结果:
达到了我们想要的运行效果。
小结:
为什么必须要重写hashcode方法,其实简单的说就是为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。相关文章推荐
- ID的生成策略(hibernate的id生成策略,主键类为什么需要实现序列化接口,同时还要重写hashCode()和equals()方法)
- ID的生成策略(hibernate的id生成策略,主键类为什么需要实现序列化接口,同时还要重写hashCode()和equals()方法)
- list\set等容器(集合)那里重写equals为什么还要重写hashCode方法
- 为什么重写equals方法还要重写hashcode方法?
- 为什么重写了equals方法后,还要重写hashcode方法
- 为什么要重写hashcode和equals方法
- Hibernate为什么要重写equals和hashCode方法
- 为什么重写equals方法,一定要重写HashCode方法?
- JAVA中重写equals()方法为什么要重写hashcode()方法?
- 整理 Object 类(为什么重写 equals 和 hashCode 方法)
- JAVA中重写equals()方法为什么要重写hashcode()方法?
- 重写equals方法时为什么需要重写hashCode方法
- JAVA中重写equals()方法为什么要重写hashcode()方法?
- 为什么重写equals方法,一定要重写HashCode方法?
- 【Java基础】JAVA中重写equals()方法为什么要重写hashcode()方法?
- JAVA中重写equals()方法为什么要重写hashcode()方法说明
- 为什么重写equals方法要重写hashCode方法
- Hibernate为什么要重写equals和hashCode方法
- HashCode的作用 以及重写equals方法为什么要重写HashCode方法?
- JAVA中重写equals()方法为什么要重写hashcode()方法?