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. D - Counting Test Gym - 101532D 字符串

    Yousef has a string s that is used to build a magical string w by repeating the string s infinitely ...

  2. C#事件の事件聚合器(二)

    WPF中时常会遇到ViewModel之间的通讯,ViewModel并不知道自己的View,但是一个View发生的更改需要通知另外一个View. 举一个例子,软件界面上有个人信息,打开一个界面更改用户的 ...

  3. 分布式锁(一) Zookeeper分布式锁

    什么是Zookeeper? Zookeeper(业界简称zk)是一种提供配置管理.分布式协同以及命名的中心化服务,这些提供的功能都是分布式系统中非常底层且必不可少的基本功能,但是如果自己实现这些功能而 ...

  4. P1460 健康的荷斯坦奶牛 Healthy(DFS)

    思路:这道题还是用了小小的剪枝,这里要注意的是该题有很多中构建树的顺序,但是,在这众多顺序中不一定都能保证输出的方案字典序最小. 构建搜索树:如图构建 剪枝,emmm,看代码: #include< ...

  5. JS(1) JavaScript 用法

    HTML 中的脚本必须位于 <script> 与 </script> 标签之间. 脚本可被放置在 HTML 页面的 <body> 和 <head> 部分 ...

  6. pytorch例子学习——TRAINING A CLASSIFIER

    参考:https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#sphx-glr-beginner-blitz-cifar1 ...

  7. centos 7 安装mqtt 修改用户名和密码

    我先新买个Centos 的系统 咱登录呢就用这个软件,主要是方便,可以少安装一些东西 根据自己的系统选择,上面的是32位的. 输入 root 回车 输入密码然后回车  输入的时候什么也不显示 输入 c ...

  8. face recognition[angular/consine-margin-based][L2-Softmax]

    本文来自<L2-constrained Softmax Loss for Discriminative Face Verification>,时间线为2017年6月. 近些年,人脸验证的性 ...

  9. 基于webpack+react+antd 项目构建

    工欲善其事必先利其器,学习React也是如此. 下面分享一篇基于webpack+react+antd 项目构建的好文章, https://blog.hduzplus.xyz/articles/2017 ...

  10. 深入浅出Tomcat/2 - Tomcat启动和停止

    Tomcat启动和停止 很明显,我们启动或停止Tomcat,一般调用的是bin下的startup.sh或shutdown.sh(以Linux为例,以下涉及到平台,若无特殊说明,一般都指Linux).我 ...