hashcode和equals方法的区别和联系
说到 hashcode就要和Java中的集合,HashSet,HashMap 关系最为密切。
首先附录两张Java的集合结构图:

图二:(上图的简化版)

从Set集合的特点说起 & Set是如何去除重复元素的:
Set:元素不可以重复,是无序的。 Set接口中的方法和Collection一致(看上面的集合框架图)。
|--HashSet: 内部数据结构是哈希表 ,是不同步的。
如何保证该集合的元素唯一性呢?
是通过对象的hashCode和equals方法来完成对象唯一性的。
hashCode值和对象的内容是存储在hash表中的。
如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。
如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
如果为true,视为相同元素,不存储。如果为false,那么视为不同元素,就进行存储。
记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。一般情况下,如果定义的类会产生很多对象,比如人(Person类),学生(Student类),书(Book类),通常都需要覆盖equals,hashCode方法。
建立对象判断是否相同的依据。

哈希算法是底层windows做的,而且针对不同的对象有不同的哈希算法运算。
java类中有的哈希算法是native用的windows底层 的,有的根据对象的不同进行了重写。
哈希表:哈希这种算法会算出很多的值,把这些值存储起来形成一个表。表中有对应关系。
哈希表的结构还是数组。只是哈希这种算法对数组进行了优化。
哈希算法有一种最常见的算法就是取余。
哈希表中不能有重复的元素。
哈希表是如何判断元素是否是重复的? 举例一种取余的HashCode算法原理:

“ab”和”ba”产生的哈希值是一样的,取余之后都是5,但是内容不一样,哈希值对这样的情况也有对应的方法,在5角标出“挂”一个”ba”。
在list集合remove和contains都要判断相不相同,都是用的equals,不用hashcode
如果到了Set集合,要删除一个元素,要依据该元素是否和该容器中的元素是否相同。HashSet依据对象的hashcode和equals。
代码举例,Set中存储自定义的类对象:
①原始的方式,不重写equals也不重写hashcode。
Person.java
 public class Person {
     private String name;
     private int age;
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public Person(String name, int age) {
         super();
         this.name = name;
         this.age = age;
     }
 }
CollectionTest.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class CollectionTest {
public static void main(String[] args) {
//单个对象
Person p1 = new Person("kxh",22);
Person p2 = new Person("kxh",22);
System.out.println("p1 == p2:" + (p1 == p2));
System.out.println("p1.equals(p2):" + p1.equals(p2));
//List集合
ArrayList<Person> arrayList = new ArrayList<Person>();
arrayList.add(new Person("lisi1",11));
arrayList.add(new Person("lisi2",12));
arrayList.add(new Person("lisi3",13));
arrayList.add(new Person("lisi4",14));
arrayList.add(new Person("lisi4",14));
//HashMap集合
HashMap<Person,Object> hashMap = new HashMap<Person,Object>();
hashMap.put(new Person("lisi1",11),new Object());
hashMap.put(new Person("lisi2",12),new Object());
hashMap.put(new Person("lisi3",13),new Object());
hashMap.put(new Person("lisi4",14),new Object());
hashMap.put(new Person("lisi4",14),new Object());
//HashSet集合
HashSet<Person> hashSet = new HashSet<Person> ();
hashSet.add(new Person("lisi1",11));
hashSet.add(new Person("lisi2",12));
hashSet.add(new Person("lisi3",13));
hashSet.add(new Person("lisi4",14));
hashSet.add(new Person("lisi4",14)); //遍历ArrayList集合
System.out.println("遍历ArrayList集合------");
Iterator arrayListIterator = arrayList.iterator();
while(arrayListIterator.hasNext()){
Person p = (Person) arrayListIterator.next();
System.out.println(p.getName()+"---"+p.getAge());
} //遍历HashMap集合
System.out.println("遍历HashMap集合------");
Set<Entry<Person, Object>> entrySet = hashMap.entrySet();
Iterator entrySetIterator = entrySet.iterator();
while(entrySetIterator.hasNext()){
Map.Entry entry = (Map.Entry)entrySetIterator.next();
System.out.println(entry.getKey()+"---"+entry.getValue());
} //遍历HashSet集合
System.out.println("遍历HashSet集合------");
Iterator hashSetIterator = hashSet.iterator();
while(hashSetIterator.hasNext()){
Person p = (Person) hashSetIterator.next();
System.out.println(p.getName()+"---"+p.getAge());
}
}
}
程序输出:
p1 == p2:false
p1.equals(p2):false
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
cn.cdv.collection.Person@52e922---java.lang.Object@1b84c92
cn.cdv.collection.Person@10dea4e---java.lang.Object@1c7c054
cn.cdv.collection.Person@1db9742---java.lang.Object@12204a1
cn.cdv.collection.Person@106d69c---java.lang.Object@a298b7
cn.cdv.collection.Person@25154f---java.lang.Object@14991ad
遍历HashSet集合------
lisi1---11
lisi2---12
lisi4---14
lisi4---14
lisi3---13
ArrayList中可以存储重复元素,HashMap中使用了两个“一样的”new Person("lisi4",14)作为key,同一个key在HashMap中只能有一个,HashSet中存储进去了两个“lisi4 14”又是集合Set ,Set集合中不会再存储相同的元素。为什么出现这种情况。
①这是Map和Set存储用户自定义对象。
②Person类继承的是Object,每new一个Person都有不同的地址值,hashcode不同,对于Set集合来说是不同的元素,对Map来说是不同的Key。
③你自己定义的只要名字和年龄一样就是不同的对象这个规则程序现在是不知道的。需要你自己去定义,完善。
②只重写equals方法,在equals方法中判断Person对象的name和age属性值是否相等来判断对象是否相同。
 public class Person extends Object {
     private String name;
     private int age;
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public Person(String name, int age) {
         super();
         this.name = name;
         this.age = age;
     }
     @Override
     public boolean equals(Object obj) {
         if(this == obj){//当判断完hashCode()方法之后hah值相同,如果内容也相同,使用==这样结束equals语句中的判断。
             return true;
         }
         if(!(obj instanceof Person)){
             throw new ClassCastException("类型错误");
         }
         //运行到这里说明两者 相等,打印输出。。。
         System.out.println(this+"....重写equals判断相同....."+obj);
         Person p = (Person)obj;
         return this.name.equals(p.name) && this.age == p.age;
     }
     public String toString(){
         return name +":"+ age;
     }
 }
程序输出:
p1 == p2:false
kxh:22....重写equals判断相同.....kxh:22
p1.equals(p2):true
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
lisi3:13---java.lang.Object@1b84c92
lisi4:14---java.lang.Object@1c7c054
lisi1:11---java.lang.Object@12204a1
lisi2:12---java.lang.Object@a298b7
lisi4:14---java.lang.Object@14991ad
遍历HashSet集合------
lisi1---11
lisi2---12
lisi4---14
lisi4---14
lisi3---13
结果分析:因为Person两个对象的age和name属性相等,而且又是通过覆盖equals方法来判断的,所示p1.equals(p2) 为true。
只重写equals方法,加入ArrayList,HashMap和HashSet的元素都全部打印出来。
③通过以上代码我们知道equals方法已经生效。接下来我们在覆盖一下hashCode方法(通过age和name属性来生成hashcode)并不覆盖equals方法,其中Hash码是通过age和name生成的。
 public class Person extends Object {
     private String name;
     private int age;
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public Person(String name, int age) {
         super();
         this.name = name;
         this.age = age;
     }
     /*
      * hashCode()方法返回的是一个int类型的值,这里根据Person类对象的特点,
      * 返回一个由name和age属性决定的值最合适。age之后乘以的数字只要不是1,都可以。
      */
     @Override
     public int hashCode() {
         System.out.println(this+".......hashCode的值:" + name.hashCode()+age*27);
         return name.hashCode()+age*27;
     }
     public String toString(){
         return name +":"+ age;
     }
 }
测试类不变,运行测试类,程序输出:
p1 == p2:false
p1.equals(p2):false
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
lisi2:12---java.lang.Object@1db9742
lisi1:11---java.lang.Object@106d69c
lisi4:14---java.lang.Object@52e922
lisi4:14---java.lang.Object@25154f
lisi3:13---java.lang.Object@10dea4e
遍历HashSet集合------
lisi2---12
lisi1---11
lisi4---14
lisi4---14
lisi3---13
上面ArrayList,HashMap,HashSet总共三组,但是只打印了两组数据的hashcode。由集合的特性可知,这两组是HashMap和HashSet添加数据时打印的,ArrayList中添加数据是不会判断hashcode值的。
我们并没有覆盖equals方法只覆盖了hashCode方法,对于HashMap两个对象中有两个new Person("lisi4",14)作为key,对于HashSet中放入有两个new Person("lisi4",14),
虽然两个对象new Person("lisi4",14)的hashCode一样,但是根据比较原则,先比较hashcode,如果hashcode相同再比较equals,很明显两个new Person("lisi4",14) 对应的值为false,所以加入到HashMap和HashSet的值全部打印出来。
④既覆盖hashCode() 又重写equals()方法
 public class Person extends Object {
     private String name;
     private int age;
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public Person(String name, int age) {
         super();
         this.name = name;
         this.age = age;
     }
     /*
      * hashCode()方法返回的是一个int类型的值,这里根据Person类对象的特点,
      * 返回一个由name和age属性决定的值最合适。age之后乘以的数字只要不是1,都可以。
      */
     @Override
     public int hashCode() {
         System.out.println(this+".......hashCode的值:" + name.hashCode()+age*27);
         return name.hashCode()+age*27;
     }
     @Override
     public boolean equals(Object obj) {
         if(this == obj){//当判断完hashCode()方法之后hah值相同,如果内容也相同,使用==这样结束equals语句中的判断。
             return true;
         }
         if(!(obj instanceof Person)){
             throw new ClassCastException("类型错误");
         }
         //运行到这里说明两者 相等,打印输出。。。
         System.out.println(this+"....重写equals判断相同....."+obj);
         Person p = (Person)obj;
         return this.name.equals(p.name) && this.age == p.age;
     }
     public String toString(){
         return name +":"+ age;
     }
 }
测试类不变,运行输出:
p1 == p2:false
kxh:22....重写equals判断相同.....kxh:22
p1.equals(p2):true
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
lisi4:14....重写equals判断相同.....lisi4:14
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
lisi4:14....重写equals判断相同.....lisi4:14
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
lisi2:12---java.lang.Object@1db9742
lisi1:11---java.lang.Object@106d69c
lisi4:14---java.lang.Object@52e922
lisi3:13---java.lang.Object@25154f
遍历HashSet集合------
lisi2---12
lisi1---11
lisi4---14
lisi3---13
通过结果可以看出,hashcode和equals都复写之后才真正保证了唯一性,复写equals方法必须复写hashcode方法。

附录另外有价值的博客:
http://blog.csdn.net/michaellufhl/article/details/5833188
http://blog.csdn.net/jiangwei0910410003/article/details/22739953
http://www.cnblogs.com/DreamDrive/p/5431725.html
http://www.cnblogs.com/lulipro/p/5628750.html
hashcode和equals方法的区别和联系的更多相关文章
- 一、基础篇--1.1Java基础-hashCode和equals方法的区别和联系
		hashCode和equals方法的区别和联系 两个方法的定义 equals(Object obj)方法用来判断两个对象是否"相同",如果"相同"则返回tr ... 
- hashCode和equals方法的区别与联系
		hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等: (1)equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比 ... 
- 关于hashcode和equals方法说明
		一.前言 我们都知道,要比较两个对象是否相等时需要调用对象的equals()方法,即判断对象引用所指向的对象地址是否相等,对象地址相等时,那么与对象相关的对象句柄.对象头.对象实例数据.对象类型数据等 ... 
- 重写hashcode和equals方法
		重写hashcode和equals方法 简乐君 2019-05-07 21:55:43 35481 收藏 191分类专栏: Java 文章标签: equals() hashcode()版权 一.前言我 ... 
- 用HashSet的add方法谈hashcode和equals方法重写
		本文主要通过用HashSet的add方法讲一下hashCode和equals方法重写.错误的地方望指正. 1.了解HashSet的add方法 了解一个方法的好办法是看源码,所以先看源码 private ... 
- Java 中正确使用 hashCode 和 equals 方法
		在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ... 
- 使用hashCode()和equals()方法 - Java
		在这篇文章中,我将指出我对hashCode()和equals()方法的理解.我将讨论它们的默认实现以及如何正确地覆盖它们.我还将使用Apache Commons包中的实用工具类来实现这些方法. has ... 
- K:java中的hashCode和equals方法
		hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals.通过翻看Object类 ... 
- Java学习从菜鸟变大鸟之一   hashCode()和equals()的本质区别和联系
		在学习java,根据视频做实例的过程中,对equals和hashcode两个方法理解稍微深刻一点,主要是它们两个很容易混淆,容易出错,自己又通过网上的资料学习,和大家分享 equals()方法 equ ... 
随机推荐
- TypeError: while_loop() got an unexpected keyword argument 'maximum_iterations'
			错误: TypeError: while_loop() got an unexpected keyword argument 'maximum_iterations' 参照https://blog.c ... 
- linux系统,服务器与服务器拷贝文件
			服务器与服务器拷贝文件命令 scp -P (服务器端口)-r 拷贝文件名称列表 远程服务器用户@远程服务器ip :(文件放置目录) 1.将本地home目录下的apache-tomcat-8.0. ... 
- cmd中sudo以后显示password不能输入密码
			文本界面还是图形界面下输入密码都不会有回显,这是为了安全考虑. 其实你不是不能输入密码只是你看不到而已,事实上你已经输入进去了,回车后就能看到效果了. 来源于:https://zhidao.baidu ... 
- rsync同步命令详解
			一.rsync命令的解释sync(Synchronize,即“同步”)为UNIX操作系统的标准系统调用,功能为将内核文件系统缓冲区的所有数据(也即预定将通过低级I/O系统调用写入存储介质的数据)写入存 ... 
- 自定义RPC框架--基于JAVA实现
			视频教程地址 DT课堂(原名颜群) 整体思路RPC(Remote Procedure Call),即远程过程调用.使用RPC,可以像使用本地的程序一样使用远程计算机上的程序.RPC使得开发分布式程序更 ... 
- 学习笔记《简明python教程》
			学习笔记<简明python教程> 体会:言简意赅,很适合新手入门 2018年3月14日21:45:59 1.global 语句 在不使用 global 语句的情况下,不可能为一个定义于函数 ... 
- php之$_SESSION的理解
			1.什么是session? Session的中文译名叫做“会话”,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个sessi ... 
- dtruss
			一.简介 系统调用跟踪工具. 二.实例 
- json-server使用及路由配置
			1.先安装node.js,node.js中包含了json-server模块 2.在angular-hello/src/app/data-base.json文件中,编辑json格式的服务数据, { &q ... 
- web工程was部署
			web.xml调整: 新增如下servlet <servlet> <servlet-name>SimpleFileServlet</servlet-name> &l ... 
