说到 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. 一、基础篇--1.1Java基础-hashCode和equals方法的区别和联系

     hashCode和equals方法的区别和联系  两个方法的定义 equals(Object obj)方法用来判断两个对象是否"相同",如果"相同"则返回tr ...

  2. hashCode和equals方法的区别与联系

    hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等: (1)equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比 ...

  3. 关于hashcode和equals方法说明

    一.前言 我们都知道,要比较两个对象是否相等时需要调用对象的equals()方法,即判断对象引用所指向的对象地址是否相等,对象地址相等时,那么与对象相关的对象句柄.对象头.对象实例数据.对象类型数据等 ...

  4. 重写hashcode和equals方法

    重写hashcode和equals方法 简乐君 2019-05-07 21:55:43 35481 收藏 191分类专栏: Java 文章标签: equals() hashcode()版权 一.前言我 ...

  5. 用HashSet的add方法谈hashcode和equals方法重写

    本文主要通过用HashSet的add方法讲一下hashCode和equals方法重写.错误的地方望指正. 1.了解HashSet的add方法 了解一个方法的好办法是看源码,所以先看源码 private ...

  6. Java 中正确使用 hashCode 和 equals 方法

    在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...

  7. 使用hashCode()和equals()方法 - Java

    在这篇文章中,我将指出我对hashCode()和equals()方法的理解.我将讨论它们的默认实现以及如何正确地覆盖它们.我还将使用Apache Commons包中的实用工具类来实现这些方法. has ...

  8. K:java中的hashCode和equals方法

      hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals.通过翻看Object类 ...

  9. Java学习从菜鸟变大鸟之一 hashCode()和equals()的本质区别和联系

    在学习java,根据视频做实例的过程中,对equals和hashcode两个方法理解稍微深刻一点,主要是它们两个很容易混淆,容易出错,自己又通过网上的资料学习,和大家分享 equals()方法 equ ...

随机推荐

  1. php面试题五之nginx如何调用php和php-fpm的作用和工作原理

    nginx如何调用php 采用nginx+php作为webserver的架构模式,在现如今运用相当广泛.然而第一步需要实现的是如何让nginx正确的调用php.由于nginx调用php并不是如同调用一 ...

  2. 牛客小白月赛12 J 月月查华华的手机(序列自动机)

    ---恢复内容开始--- 题目来源:https://ac.nowcoder.com/acm/contest/392/J 题意: 题目描述 月月和华华一起去吃饭了.期间华华有事出去了一会儿,没有带手机. ...

  3. Springboot学习06-Spring AOP封装接口自定义校验

    Springboot学习06-Spring AOP封装接口自定义校验 关键字 BindingResult.Spring AOP.自定义注解.自定义异常处理.ConstraintValidator 前言 ...

  4. Sublime Text3快捷键大全

    选择类 Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本. Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个栗子:快速选中并更改所有相同的变量名.函数 ...

  5. php设计模式-工厂模式(一)

    <?php abstract class Creator{ /* startFactory 返回一个具体的产品 factoryMethod 返回对象 */ protected abstract ...

  6. 服务器重新启动,oracle数据库重新连接问题

    最近服务器使用期限到了,用户无法登陆,联系了网络的同事,重新开启了服务器,用户可以重新登陆了,但是在使用oracle数据库的时候,发现报了以下的错误: 很明显是数据库服务没有启动,解决这个问题分两步: ...

  7. 远程连接centos6.5

    安装上传下载工具: 直接yum -y install lrzsz 下载数据到本地下载目录:sz filename1 filename2 … 上传数据到远程:执行rz –be 命令,客户端会弹出上传窗口 ...

  8. 目录命令(cd)

    cd 命令: // 描述: (Change Directory) 更改当前目录或显示当前目录的名称. 如果仅使用驱动器号(例如,cd C :),则cd将显示指定驱动器中当前目录的名称. 如果不带参数使 ...

  9. Python3个人学习笔记--每天一点一滴成长!

    简单的while循环:输入一个数字,while获取该数字,并输出该数字. 例子:猜幸运数字是多少? lucky_num = int(input("number:")) a = 0 ...

  10. python 实践项目 强密码检测

    需求:写一个函数,它使用正则表达式,确保传入的口令字符串是强口令.强口令的定义是:长度不少于 8 个字符,同时包含大写和小写字符,至少有一位数字.你可能需要用多个正则表达式来测试该字符串,以保证它的强 ...