Java 集合实现类,无论是HashSet、HashMap等所有的Hash算法实现的集合类(后面简称Hash集合),加入的对象必须实现 hashCode() 与 equals() 方法,稍微不同的地方是:HashSet 需要对整个对象实现两个方法,而HashMap 只需要对作为key的对象实现这两个方法。因为向Hash集合存入一个元素时,Hash集合会调用该对象的hashCode()方法来得到该对象的hashCode的值,然后根据hashCode的值决定该对象在Hash集合中存储位置。如果两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,Hash集合将会把它们视为不同的对象,两个元素都可以添加到Hash集合里。所以Hash集合判断两个元素是否相等的标准是:两个对象通过equals()方法比较相等,并且两个元素的hashCode()方法返回值也相等。

重写hashCode()方法的规则:

① 同一个对象多次调用hashCode()方法返回值相同

② 两个对象通过equals()方法比较返回true 时,这两个对象的hashCode()方法返回相等的值

③ 对象中用于equals() 方法比较标准的变量,都应该用于计算hashCode值

重写hashCode方法一般步骤

① 把对象内每个有意义的变量计算出一个int 型的hashCode值

② 用第1步计算出来的多个hashCode值组合成一个hashCode值返回

③ 为了避免直接相加产生偶然相等,可以通过为各变量的hashCode值乘以任意质数再相加

案例:

① 首先来看一个没有重写equals() 与 hashCode() 方法的对象,添加到HashSet 时会出现什么情况。

我们创建一个Person类,包含两个成员变量,id 与 name,如果两个对象的这两个属性都不等我们才认为它们是不同的对象,我们没有重写equals() 与 hashCode() 方法,但是我们希望HashSet 集合对同一个对象只能保存一份,有相同对象加入时,加入失败。

 class Person
{
private String name;
private int id; public Person()
{} public Person(int id,String name)
{
this.id = id;
this.name = name;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
         HashSet haSe = new HashSet();

         //相同id与name的添加两次
haSe.add(new Person(1001, "latiny"));
haSe.add(new Person(1001, "latiny")); //遍历HashSet
for(Object obj:haSe)
{
Person p = (Person)obj;
System.out.println(p.getId()+" "+p.getName()+" "+p.hashCode());
}

  输出结果:

1001 latiny 355165777
1001 latiny 1807500377

可以看到name与id相同的两个对象都成功的加入到了HashSet集合,因为它们的hashCode值不一样,这显然没有达到预期的效果,如果要实现预期的效果需要重写Person类 equals() 与 hashCode() 方法。

② 重写Person类equals() 与 hashCode() 方法

在上面的Person类添加如下代码:

     //重写hashCode() 方法
public int hashCode() {
int hash = 7;
hash = hash*31 + this.id*11 + this.name.hashCode()*31;
return hash;
} //重写equals() 方法
public boolean equals(Object obj) {
if(this==obj)
{
return true;
}
//如果两个对象的id与name都相等,则两个对象相等
if(obj.getClass()==Person.class)
{
Person p=(Person)obj;
return this.id==p.id && this.name==p.name;
}
else
{
return false;
}
}

  

  重新执行代码:

         HashSet haSe = new HashSet();

         //相同id与name的对象添加两次
haSe.add(new Person(1001, "latiny"));
System.out.println(haSe.add(new Person(1001, "latiny"))); //遍历HashSet
for(Object obj:haSe)
{
Person p = (Person)obj;
System.out.println(p.getId()+" "+p.getName()+" "+p.hashCode());
}

  输出结果: 

false
1001 latiny -46445433

可以看到 id与 name 相同的两个对象只有一个加入到了HashSet集合,第二次添加时失败,因为我们重写了equals() 与 hashCode() 方法,只要Person类对象id与name都相同,equals()返回true,hashCode() 返回值相同,HashSet集合就会将这两个对象视为同一个对象。

HashMap 的使用与HashSet类似,作为key的对象也需要重写 equals() 与 hashCode() 方法,不然也会出现将相同对象作为key值成功插入到HashMap中。

① 未重写 equals() 与 hashCode() 方法

 class Person
{
private String name;
private int id; public Person()
{} public Person(int id,String name)
{
this.id = id;
this.name = name;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} }

  

  将Person类作为HashMap的key

         HashMap<Person, String> hash = new HashMap<Person, String>();
hash.put(new Person(1, "latiny"), "周瑜");
hash.put(new Person(1, "latiny"), "曹操");
hash.put(new Person(1, "latiny"), "刘禅"); //foreach遍历HashMap
for(Object key:hash.keySet())
{
String name = hash.get(key);
System.out.println(key+"-->"+name+" "+key.hashCode());
} Object obj = hash.get(new Person(1, "latiny"));
String name = (String) obj;
System.out.println(name);

  输出结果:

com.latiny.set.Person@544a5ab2-->刘禅 1414159026
com.latiny.set.Person@152b6651-->曹操 355165777
com.latiny.set.Person@6bbc4459-->周瑜 1807500377
null

可以看到,id 与 name 相同的Person对象作为 key 重复添加到HashMap集合中。

更为严重的问题是,当我们想要以相同的 id 与 name的Person对象作为key去获取value 时,结果竟然是null,为什么呢?因为这个Person对象的id 与 name 与之前的三个对象相同,但是在内存中它却是一个新的对象,有自己独立的空间,即有自己独立的hashCode值,由于我们没有重写hashCode方法,它的hashCode计算方法还是按照Object这个类来实现的。

② 重写 equals() 与 hashCode() 方法之后

 class Person
{
private String name;
private int id; public Person()
{} public Person(int id,String name)
{
this.id = id;
this.name = name;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} //重写hashCode() 方法
public int hashCode() {
int hash = 7;
hash = hash*31 + this.id*11 + this.name.hashCode()*31;
return hash;
} //重写equals() 方法
public boolean equals(Object obj) {
if(this==obj)
{
return true;
}
//如果两个对象的id与name都相等,则两个对象相等
if(obj.getClass()==Person.class)
{
Person p=(Person)obj;
return this.id==p.id && this.name==p.name;
}
else
{
return false;
}
} }

  执行测试代码

         HashMap<Person, String> hash = new HashMap<Person, String>();
hash.put(new Person(1, "latiny"), "周瑜");
hash.put(new Person(1, "latiny"), "曹操");
hash.put(new Person(1, "latiny"), "刘禅"); //foreach遍历HashMap
for(Object key:hash.keySet())
{
String name = hash.get(key);
System.out.println(key+"-->"+name+" "+key.hashCode());
} Object obj = hash.get(new Person(1, "latiny"));
String name = (String) obj;
System.out.println(name);

  输出结果:

com.latiny.set.Person@fd3b218f-->刘禅 -46456433
刘禅

可以看到,最后一次添加的元素将之前添加的都覆盖了,因为我们重写的方法判断如果Person类的id与name相同,equals()返回true, hashCode() 返回相同的值,HashMap认定它们key值相同,会将之前加入的元素覆盖。我们也可以将具有相同的id与name的Person类作为key值去访问value。

Java Hash集合的equals()与hashCode() 方法的更多相关文章

  1. java基础(十六)----- equals()与hashCode()方法详解 —— 面试必问

    本文将详解 equals()与hashCode()方法 概述 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) publi ...

  2. 集合之equals与hashCode方法

    一  equals equals方法是Object级的,默认对比两个对象的内存地址,很多类都重写了该方法,对比对象的实际内容,一般对比同一类对象相同属性的属性值是否相同. 二 hashCode 1.哈 ...

  3. List去重为什么要写equals(),hashCode()方法

    一,各个集合的特点: Collection(集合):容器,用于存放对象(引用类型.基本类型需要自动装箱) List(列表):元素有序,元素可以重复 (有索引). 通过元素的equals()方法判断是否 ...

  4. 为什么要重写equals和hashcode方法

    equals hashcode  当新建一个java类时,需要重写equals和hashcode方法,大家都知道!但是,为什么要重写呢? 需要保证对象调用equals方法为true时,hashcode ...

  5. java集合(3)- Java中的equals和hashCode方法详解

    参考:http://blog.csdn.net/jiangwei0910410003/article/details/22739953 Java中的equals方法和hashCode方法是Object ...

  6. Java中的equals和hashCode方法

    本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...

  7. Java提高篇——equals()与hashCode()方法详解

    java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...

  8. Java中的equals和hashCode方法详解

    Java中的equals和hashCode方法详解  转自 https://www.cnblogs.com/crazylqy/category/655181.html 参考:http://blog.c ...

  9. java重写equals和hashCode方法

    一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...

随机推荐

  1. flex布局快速成型(原创)

    最近我根据一个UI设计,耗时3h快速实现较复杂页面布局,如上图.根据这份UI设计图,实现代码如下,暂不考虑具体细节,先以成型为主: <!DOCTYPE html> <html> ...

  2. Django-rest-framework 接口实现 Serializer 使用

    Django接口实现 DRF 使用 以下模块 实现 json数据 序列化 博客: https://www.cnblogs.com/liwenzhou/p/9959979.html Django RES ...

  3. SQLite 线程安全和并发

    SQLite 与线程 SQLite 是线程安全的. 线程模型 SQLite 支持如下三种线程模型 单线程模型 这种模型下,所有互斥锁都被禁用,同一时间只能由一个线程访问. 多线程模型 这种模型下,一个 ...

  4. python笔记31-使用ddt报告出现dict() -> new empty dictionary dict(mapping) 问题解决

    使用ddt框架生成html报告的时候,出现:dict() -> new empty dictionary dict(mapping) -> new dictionary initializ ...

  5. python3.6在linux持久运行django

    最近线上运行一个OnlineJudgeServer的项目,通过python manage.py runserver 0.0.0.0:8090运行,如果关闭当前窗口,实际就相当于关闭了这个进程. 之前说 ...

  6. P1440 求m区间内的最小值--洛谷luogu

    题目描述 一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值.若前面的数不足m项则从第1个数开始,若前面没有数则输出0. 输入输出格式 输入格式: 第一行两个 ...

  7. 1-STM32带你入坑系列(STM32介绍)

    由于自己的物联网开发板上的单片机是用的STM32,但是有些朋友没有用过,所以我将用这块开发板,带着大家入门STM32 先介绍一下STM32,我是在大三下学期的时候开始接触STM32,当时是想做一个小车 ...

  8. MySQL报错: java.sql.SQLException: Column count doesn't match value count at row 1

    这个是今天写新项目的是后,写插入语句测试用例的时候报这个错误, 其实错误的原因就是插入语句的前面的列和后面的值 的个数不对. 错在此,仔细检查一下,看看少了哪一个,然后修改就可以了.

  9. .NET Core中复制源文件夹下的所有内容到新文件夹

    .NET Core中没有原生的复制文件夹方法,我们可以自己写个: 新建一个.NET Core控制台项目,示例代码如下: using System; using System.IO; namespace ...

  10. Ext 编辑 comobox编辑源只能选择一个

    storePrType.DataSource = optsvc.Select("28").Where(r => r.OptionID == cmbEngineeringPrT ...