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. Maven中POM.XML详解

    转自https://blog.csdn.net/jariwsz/article/details/19554137 我们先看一个简单的例子: <project xmlns="http:/ ...

  2. C#基础知识之托管代码和非托管代码

    什么是托管代码(managed code)? 托管代码(Managed Code)就是中间语言(IL)代码,在公共语言运行库(CLR)中运行.编译器把代码编译成中间语言,当方法被调用时,CLR把具体的 ...

  3. (转)ElasticSearch教程——汇总篇

    https://blog.csdn.net/gwd1154978352/article/details/82781731 环境搭建篇 ElasticSearch教程——安装 ElasticSearch ...

  4. y7000笔记本 darknet-yolo安装与测试(Ubuntu18.04+Cuda9.0+Cudnn7.1)

    环境配置看上一贴 https://www.cnblogs.com/clemente/p/10386479.html 1 安装darknet 1-1 克隆darknet repo git clone h ...

  5. 「PSR 规范」PSR-2 编码风格规范

    所有 PSR 规范请见:https://learnku.com/docs/psr  https://learnku.com/laravel/t/2079/psr-specification-psr-2 ...

  6. 3-jsp 内置对象、转发与重定向

    1.request:请求常用api: getParameter("name"):获取页面表单单个元素的值 getParameterValues("name"): ...

  7. mysql 查看当前使用的配置文件my.cnf的方法

    my.cnf是mysql启动时加载的配置文件,一般会放在mysql的安装目录中,用户也可以放在其他目录加载. 安装mysql后,系统中会有多个my.cnf文件,有些是用于测试的. 使用locate m ...

  8. ABAQUS复合材料

    转自)http://meiniyuan.blog.sohu.com/134141022.html 有很多帖子相当的不错,介绍了复合材料研究的基本的原理和方法,发现好的帖子再更新:0 c3 n0 ~+ ...

  9. 深入理解 Getter和Setter 对对象的属性监听

    阅读目录 一:理解普通对象在声明时添加 get.set 二:Object.prototype.__defineGetter__ 和 Object.prototype.__defineSetter__ ...

  10. python:unittest之discover()方法批量执行用例

    自动化测试过程中,自动化覆盖的功能点和对应测试用例之间的关系基本都是1 VS N,如果每次将测试用例一个个单独执行,不仅效率很低, 无法快速反馈测试结果,而且维护起来很麻烦.在python的单元测试框 ...