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. linux历史命令查找快捷方式

      一.回到上次操作的目录# cd -进入上次访问目录 二.历史命令搜索操作快捷键:[Ctrl + r], [Ctrl + p], [Ctrl + n] 在终端中按捉 [Ctrl] 键的同时 [r] ...

  2. Vue项目使用webstorm开发 保存浏览器不自动更新问题

    1.首先进去编辑器的设置页面 2.按照以下步骤进行操作,把如下选项的√去掉即可:

  3. C - 继续畅通工程 最小生成树

    省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经 ...

  4. zuul重试配置

    #retry#该参数用来开启重试机制spring.cloud.loadbalancer.retry.enabled=true#断路器的超时时间,断路器的超时时间需要大于ribbon的超时时间,不然不会 ...

  5. Git 遇到的坑

    ssh出错 gitlab服务器添加完公钥之后,ssh服务器然后报了这个错误 sign_and_send_pubkey: signing failed: agent refused operation ...

  6. tensorflow 模型保存与加载 和TensorFlow serving + grpc + docker项目部署

    TensorFlow 模型保存与加载 TensorFlow中总共有两种保存和加载模型的方法.第一种是利用 tf.train.Saver() 来保存,第二种就是利用 SavedModel 来保存模型,接 ...

  7. C# Lambda 表达式学习之(三):动态构建类似于 c => c.Age == null || c.Age > 18 的表达式

    可能你还感兴趣: 1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到 2. C# Lambda 表达式学习之(二):LambdaExp ...

  8. mybatis错误之org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    玩了MyBatis差不多有两年了,中间也玩过MyBatis-Plus,这个MyBatis-Plus其实与MyBatis的区别并不大.今天写博客业务代码的时候,犯一个初学者犯过的错误. 错误信息如下:o ...

  9. 计算机网络#关于HDLC#

    HDLC(High-Level Data Link Control,高级数据链路控制):面向比特型的协议 主站(发送命令).从站(响应命令).复合站(既可发送命令,也可响应命令) HDLC链路配置方式 ...

  10. Scarpy 起始url 自定义代理 自定义去重规则

    - start_urls - 内部原理 """ scrapy引擎来爬虫中去起始的URL: 1. 调用start_requests并获取返回值 2. v = iter(返回 ...