Java Hash集合的equals()与hashCode() 方法
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() 方法的更多相关文章
- java基础(十六)----- equals()与hashCode()方法详解 —— 面试必问
本文将详解 equals()与hashCode()方法 概述 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) publi ...
- 集合之equals与hashCode方法
一 equals equals方法是Object级的,默认对比两个对象的内存地址,很多类都重写了该方法,对比对象的实际内容,一般对比同一类对象相同属性的属性值是否相同. 二 hashCode 1.哈 ...
- List去重为什么要写equals(),hashCode()方法
一,各个集合的特点: Collection(集合):容器,用于存放对象(引用类型.基本类型需要自动装箱) List(列表):元素有序,元素可以重复 (有索引). 通过元素的equals()方法判断是否 ...
- 为什么要重写equals和hashcode方法
equals hashcode 当新建一个java类时,需要重写equals和hashcode方法,大家都知道!但是,为什么要重写呢? 需要保证对象调用equals方法为true时,hashcode ...
- java集合(3)- Java中的equals和hashCode方法详解
参考:http://blog.csdn.net/jiangwei0910410003/article/details/22739953 Java中的equals方法和hashCode方法是Object ...
- Java中的equals和hashCode方法
本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...
- Java提高篇——equals()与hashCode()方法详解
java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...
- Java中的equals和hashCode方法详解
Java中的equals和hashCode方法详解 转自 https://www.cnblogs.com/crazylqy/category/655181.html 参考:http://blog.c ...
- java重写equals和hashCode方法
一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...
随机推荐
- Maven中POM.XML详解
转自https://blog.csdn.net/jariwsz/article/details/19554137 我们先看一个简单的例子: <project xmlns="http:/ ...
- C#基础知识之托管代码和非托管代码
什么是托管代码(managed code)? 托管代码(Managed Code)就是中间语言(IL)代码,在公共语言运行库(CLR)中运行.编译器把代码编译成中间语言,当方法被调用时,CLR把具体的 ...
- (转)ElasticSearch教程——汇总篇
https://blog.csdn.net/gwd1154978352/article/details/82781731 环境搭建篇 ElasticSearch教程——安装 ElasticSearch ...
- 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 ...
- 「PSR 规范」PSR-2 编码风格规范
所有 PSR 规范请见:https://learnku.com/docs/psr https://learnku.com/laravel/t/2079/psr-specification-psr-2 ...
- 3-jsp 内置对象、转发与重定向
1.request:请求常用api: getParameter("name"):获取页面表单单个元素的值 getParameterValues("name"): ...
- mysql 查看当前使用的配置文件my.cnf的方法
my.cnf是mysql启动时加载的配置文件,一般会放在mysql的安装目录中,用户也可以放在其他目录加载. 安装mysql后,系统中会有多个my.cnf文件,有些是用于测试的. 使用locate m ...
- ABAQUS复合材料
转自)http://meiniyuan.blog.sohu.com/134141022.html 有很多帖子相当的不错,介绍了复合材料研究的基本的原理和方法,发现好的帖子再更新:0 c3 n0 ~+ ...
- 深入理解 Getter和Setter 对对象的属性监听
阅读目录 一:理解普通对象在声明时添加 get.set 二:Object.prototype.__defineGetter__ 和 Object.prototype.__defineSetter__ ...
- python:unittest之discover()方法批量执行用例
自动化测试过程中,自动化覆盖的功能点和对应测试用例之间的关系基本都是1 VS N,如果每次将测试用例一个个单独执行,不仅效率很低, 无法快速反馈测试结果,而且维护起来很麻烦.在python的单元测试框 ...