==和equals扩展-->重写equals()和hashcode()
2016-05-17 22:31
239 查看
上一篇博文提及了==和equals的比较,提到==比较的是引用,而equals比较的是具体引用的值。随着深入的了解,这只是通过代码比较浅地理解了这二者方法在实际应用中的用法,继续深入,不得不提及重写equals()和hashcode()。
首先,我们在初学的时候,听的比较多的说法是String类是重写了equals()和hashcode(),当然初学的时候只是当成了口诀一般牢记在心(八大基本数据类型也重写了equals()和hashcode())。随着深入的学习和应用,我们得理解为什么要重写这两个方法。
首先看一下java.lang包中Object类源码中的equals()和hashcode()。
equals:
hashcode:
然后在看看String中重写的equals()和hashcode()。
equals:
2.若地址不同,则两个String对象中每个字符,这样就解释了equals的作用。
hashcode:
那么为什么在实际中要重写equals()和hashcode()。
下面看一个简单的例子。
输出的结果是
这时候重写equals()和hashcode()排上用场了,改写Person类代码如下:
输出结果:
继续挖一会^ _^....
这时,如果Person类不重写hashcode(),而只重写equals(),按照原来的分析,这个时候在main函数添加上两行关于Tom的比较(其余代码不变)
而重写了hashcode()后,得出了我们想要的结果:
那么现在按照我自己的理解来解释下为什么重写equals必须重写hashcode,这个问题的我们就从上例中用到的HashSet来作为切入点展开。
java中集合类总的来说有List和Set,我们知道List是有序,元素可以重复,而Set元素无序,不可重复。那么Set如何保证其不可重复呢,就如上例中重写的equals那般,进行比较来判断,但是问题来了,如果只是3个对象,比较是很快的,但是如果此时Person类有成千上万个对象,那equals调用的次数就大大增加,此时的开销就很大了。而采用hash表来判断两个对象是否是同一个对象(使用hashcode来返回对象存储的物理地址),效率就大大增加了。这样我们往Set中添加新元素,通过hashcode把它的物理地址定位好,然后比较hashcode也是就比较物理地址,想想效率也是很高的。
现在可以总结下
1.equals为true,那么hashcode必须相同
2.equals为false,那么hashcode不一定相同(hashcode生成的时候,可能产生冲突,凑巧两个不同地址的hashcode一样,当然我觉得概率不大)
3.hashcode不同,equals必为false
以上只是自己的总结,有不对的地方希望大家能指正,帮我指出一个错误,对我来说都是一个进步。
首先,我们在初学的时候,听的比较多的说法是String类是重写了equals()和hashcode(),当然初学的时候只是当成了口诀一般牢记在心(八大基本数据类型也重写了equals()和hashcode())。随着深入的学习和应用,我们得理解为什么要重写这两个方法。
首先看一下java.lang包中Object类源码中的equals()和hashcode()。
equals:
public boolean equals(Object obj) { return (this == obj); }我们可以看出,equals()在此时其实只是进行了==的比较(地址比较),而不是上篇博文中提到的比较对象的内容。
hashcode:
public native int hashCode();而hashcode()更是没有进行任何的逻辑操作,用native修饰,调用了其实是hash算法。
然后在看看String中重写的equals()和hashcode()。
equals:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }1.其首先比较了引用的地址,如果相同,则为true;
2.若地址不同,则两个String对象中每个字符,这样就解释了equals的作用。
hashcode:
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; }同样的hashcode运用hash算法,保证了两个String对象有一样的hash码。
那么为什么在实际中要重写equals()和hashcode()。
下面看一个简单的例子。
package com.Test; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class Test { public void main(String[] args) { Set<Person> set = new HashSet<Person>(); set.add(new Person("Tom", "12")); set.add(new Person("Jude", "11")); set.add(new Person("Tom", "12")); Iterator<Person> it = set.iterator(); while (it.hasNext()) { System.out.println(it.next().toString()); } } class Person { public String name; public String age; public Person(String name, String age) { super(); this.name = name; this.age = age; } @Override public String toString() { // TODO Auto-generated method stub return name + "--" + age; } } }
输出的结果是
Jude--11 Tom--12 Tom--12如果这段代码起到的是一个录入人员信息的功能,而两个Tom其实是同一个人,只是录入的时候误操作录入两遍,那么这显然不是我们想要的结果。
这时候重写equals()和hashcode()排上用场了,改写Person类代码如下:
class Person { public String name; public String age; public Person(String name, String age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { // TODO Auto-generated method stub return name + "--" + age; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub if (obj instanceof Person) { if (((Person) obj).getName() == this.name && ((Person) obj).getAge() == age) { return true; } } return false; } @Override public int hashCode() { // TODO Auto-generated method stub return this.name.hashCode()+this.age.hashCode(); } }
输出结果:
Jude--11 Tom--12这样就是我们想要的结果了。
继续挖一会^ _^....
这时,如果Person类不重写hashcode(),而只重写equals(),按照原来的分析,这个时候在main函数添加上两行关于Tom的比较(其余代码不变)
System.out.println("Tom equals: "+(new Person("Tom", "12").equals(new Person("Tom", "12")))); System.out.println("Tom ==: "+(new Person("Tom", "12").equals(new Person("Tom", "12"))));得出结果
Tom equals: true Tom ==: true Tom--12 Tom--12 Jude--11这显然违背了我们的初衷,既然Tom是同一个Tom,为什么在set中会有两个Tom呢?
而重写了hashcode()后,得出了我们想要的结果:
Tom equals: true其实刚才重写了equals而没重写hashcode,在java中是不规范的,从上一例中就可以看出重写equals必须重写hashcode的必要了。
Tom ==: true
Jude--11 Tom--12
那么现在按照我自己的理解来解释下为什么重写equals必须重写hashcode,这个问题的我们就从上例中用到的HashSet来作为切入点展开。
java中集合类总的来说有List和Set,我们知道List是有序,元素可以重复,而Set元素无序,不可重复。那么Set如何保证其不可重复呢,就如上例中重写的equals那般,进行比较来判断,但是问题来了,如果只是3个对象,比较是很快的,但是如果此时Person类有成千上万个对象,那equals调用的次数就大大增加,此时的开销就很大了。而采用hash表来判断两个对象是否是同一个对象(使用hashcode来返回对象存储的物理地址),效率就大大增加了。这样我们往Set中添加新元素,通过hashcode把它的物理地址定位好,然后比较hashcode也是就比较物理地址,想想效率也是很高的。
现在可以总结下
1.equals为true,那么hashcode必须相同
2.equals为false,那么hashcode不一定相同(hashcode生成的时候,可能产生冲突,凑巧两个不同地址的hashcode一样,当然我觉得概率不大)
3.hashcode不同,equals必为false
以上只是自己的总结,有不对的地方希望大家能指正,帮我指出一个错误,对我来说都是一个进步。
相关文章推荐
- 城市(地区)行政区划代码
- mysql数据库管理工具navicat for mysql怎么用
- R语言-变量的基本操作
- 编程随笔【2016年5月16日】
- Android学习笔记--《第一行代码Android》273页代码解密
- 第12周项目1:实现复数类中的运算符重载(1)
- 话说一个滴滴司机
- POJ 2110 Mountain Walking(二分+BFS)
- python range() 和xrange()的区别
- 查看SQL SERVER中指定数据库的每个表的数据量和每行记录所占用的空间
- B树、B-树、B+树、B*树
- 超出文本显示省略号(包括一行和多行)
- R语言基本操作函数(1)变量的基本操作
- Mybatis学习——关联表查询
- 1.4 Eclipse的安装和使用
- 调整数组顺序使奇数位于偶数前面
- javaScript立即执行函数
- POJ3189 Steady Cow Assignment(二分图多重匹配)
- HDU2186(多重背包问题)
- 在windows下编译nginx