Java中Set的contains()方法 —— hashCode与equals方法的约定及重写原则

最近写项目的时候遇到了这个问题,就是我在一个ArrayList里放了一个很多个vo,每当我要在里面添加vo的时候,我都要判断一下,这个list里是否已经存在,若是存在,则不添加。虽然知道是用contains()方法,但用了之后发现一直都是false,就是无论怎么判断都是会插进去。

为了解决这个问题,看了很多相关的博文,感觉这篇翻译的文章真的解决了问题,文章后面附着我最后是如何解决的。

因为解决contains()的问题要牵扯到hashCode()和equals()方法,这有篇文章,可以帮助了解一下:
equals()与hashCode()方法协作约定


本文主要讨论 集合Set 中存储对象的 hashCode 与 equals 方法应遵循的约束关系.

新手对Set中contains()方法的疑惑

import java.util.HashSet;class Dog{String color;public Dog(String s){color = s;}
}public class SetAndHashCode {public static void main(String[] args) {HashSet<Dog> dogSet = new HashSet<Dog>();dogSet.add(new Dog("white"));dogSet.add(new Dog("white"));System.out.println("We have " + dogSet.size() + " white dogs!");if(dogSet.contains(new Dog("white"))){System.out.println("We have a white dog!");}else{System.out.println("No white dog!");}   }
}

上述代码的输出为:

We have 2 white dogs!
No white dog!

程序中添加了两只白色的小狗到集合dogSet中. 且 size()方法显示有2只白色的小狗.但为什么用 contains()方法来判断时却提示没有白色的小狗呢?

Set的contains(Object o) 方法详解

Java的API文档指出: 当且仅当 本set包含一个元素 e,并且满足(o==null ? e==null : o.equals(e))条件时,contains()方法才返回true. 因此 contains()方法 必定使用equals方法来检查是否相等.

需要注意的是: set 中是可以包含 null值的(常见的集合类都可以包含null值). 所以如果添加了null,然后判断是否包含null,将会返回true,代码如下所示:

HashSet<Dog> a = new HashSet<Dog>();
a.add(null);
if(a.contains(null)){System.out.println("true");
}

Java的根类Object定义了 public boolean equals(Object obj) 方法.因此所有的对象,包括数组(array,[]),都实现了此方法。

在自定义类里,如果没有明确地重写(override)此方法,那么就会使用Object类的默认实现.即只有两个对象(引用)指向同一块内存地址(即同一个实际对象, x==y为true)时,才会返回true。

如果把Dog类修改为如下代码,能实现我们的目标吗?

class Dog{String color;public Dog(String s){color = s;}//重写equals方法, 最佳实践就是如下这种判断顺序:public boolean equals(Object obj) {if (!(obj instanceof Dog))return false;   if (obj == this)return true;return this.color == ((Dog) obj).color;}}

英文答案是: no.

问题的关键在于 Java中hashCode与equals方法的紧密联系. hashCode() 是Object类定义的另一个基础方法.

equals()与hashCode()方法之间的设计实现原则为:

如果两个对象相等(使用equals()方法),那么必须拥有相同的哈希码(使用hashCode()方法).

即使两个对象有相同的哈希值(hash code),他们不一定相等.意思就是: 多个不同的对象,可以返回同一个hash值.

hashCode()的默认实现是为不同的对象返回不同的整数.有一个设计原则是,hashCode对于同一个对象,不管内部怎么改变,应该都返回相同的整数值.

在上面的例子中,因为未定义自己的hashCode()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。

解决办法

class Dog{String color;public Dog(String s){color = s;}//重写equals方法, 最佳实践就是如下这种判断顺序:public boolean equals(Object obj) {if (!(obj instanceof Dog))return false;   if (obj == this)return true;return this.color == ((Dog) obj).color;}public int hashCode(){return color.length();//简单原则}
}

但是上面的hashCode实现,要求Dog的color是不变的.否则会出现如下的这种困惑:

import java.util.HashSet;
import java.util.Set;public class TestContains {public static final class Person{private String name = "";public Person(String n) {setName(n);}public String getName() {return name;}public void setName(String name) {this.name = (name==null)? "" : name;}@Overridepublic int hashCode() {// 请考虑是否值得这么做,因为此时name是会变的.return name.length();// 推荐让name不可改变}@Overridepublic boolean equals(Object obj) {if(!(obj instanceof Person)){return false;}if(obj == this){return true;}return this.name.equals(((Person)obj).name);}};public static void main(String[] args) {Set<Person> persons = new HashSet<Person>();//Person person = new Person("tiemao");persons.add(person);// 修改name, 则依赖hash的集合可能失去作用person.setName("ren");// 同一个对象,居然是false,原因是我们重写了hashCode,打破了hashCode不变的基本约定boolean has = persons.contains(person);int size = persons.size();System.out.println("has="+has); // has=false.System.out.println("size="+size);// size=1}
}

以下是我遇到的问题:

List<UserVo> userlis = new ArrayList<>();

UserVo是一个类,userlist是一个ArrayList,我在每次往userlis中add数据的时候,我都要先判断一下,userlis中是不是已经存在同样的数据,如果存在,则不插入,如果不存在,我就add就去。

起初我没有重写equals()和hashCode()方法,每次的判断都是false,就是都能插进去。以下是在UserVo中重写了这两个方法,问题完美解决。

其中ID是UserVo是唯一标识符,比较两个Vo是否相等比较id就好了,当然,每个人写的都不相同,仅供参考。


翻译人员:铁锚
翻译时间: 2013年11月5日
翻译连接:Java中Set的contains()方法
原文链接: Java hashCode() and equals() Contract for the contains(Object o) Method of Set

参考文章:

http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html

相关阅读

  1. Java equals() and hashCode() Contract

  2. HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap

  3. Java: Find all callers of a method – get all methods that call a particular method

  4. 理解Java机制最受欢迎的8幅图

Java中Set的contains()方法相关推荐

  1. 在java中下列描述错误的是_在 JAVA 中 , 关于类的方法 , 下列描述错误的是 ()._学小易找答案...

    [多选题]价值的特性是 [简答题]输入任一字符串,统计其中数字,字母及其它字符个数 .(25分) [填空题]1.产品整体包括哪五个基本层次 2核心层次产品最基本的层次,是产品的_____ [单选题]纸 ...

  2. java中读取文件的方法

    总结一下java中读取文件的方法: 方法一(逐行的读取文件内容): private FileReader fileReader; private BufferedReader bufferedRead ...

  3. JAVA中线程同步的方法(7种)汇总

    JAVA中线程同步的方法(7种)汇总 同步的方法: 一.同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法. ...

  4. Java中wait和sleep方法的区别

    1.两者的区别 这两个方法来自不同的类分别是Thread和Object 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法(锁代码块和方法锁). wait ...

  5. 详细讲解Java中log4j的使用方法

    详细讲解Java中log4j的使用方法 作者: 网络 来源: 日期: 2008-1-3 23:40:24 1.Log4j是什么? Log4j可以帮助调试(有时候debug是发挥不了作 用的)和分析,要 ...

  6. php url乱码java接收,java中url乱码解决方法

    java中url乱码解决方法:(推荐:java视频教程) 1.将字符串转码:newString("xxxxx".getBytes("iso-8859-1"),& ...

  7. java中容易混淆的方法_java中容易混淆的区别

    本文会随时更新一些java中容易混淆的关键字或者知识点,如有偏见之处,望留言! final和static的差别: 1,final的英语意思"最后的",在java中修饰类,方法和变量 ...

  8. Java中的String.hashCode()方法可能有问题?

    过去几天,我一直在浏览Reddit上的一篇文章.这篇文章看得我要抓狂了.文章指出,Java中的String.hashCode()方法(将任意长度的字符串对象映射成32位int值)生成的哈希值存在冲突. ...

  9. 遍历Java中的列表的方法

    本文翻译自:Ways to iterate over a list in Java Being somewhat new to the Java language I'm trying to fami ...

  10. java 需要返回类型_在Java中,当一个方法不需要返回数据时返回类型必须是

    [问答题]在滑动轴承中什么是瓦背?其特点有哪些? [多选题]起重机采用变频调速改造后,有哪些效果? [判断题]当热继电器动作不准确时,可用弯折双金属片的方法来调整. [单选题]X62W型万能铣床进给电 ...

最新文章

  1. Java中synchronized和Lock的区别
  2. Open suse下 vi 语法加亮设置 显示
  3. 为什么要设置Java环境变量(详解)
  4. 老的消息中间件投递失败的类型值_图文结合了解一下Java消息中间件的概述
  5. Landsat中国西北地区行列号Shapefile图层对照(附行列号Shapefile下载)
  6. 论文笔记(Neural Collaborative Filtering)
  7. LwIP之套接字接口
  8. 有些新手小白创业者开奶茶店,为什么总是容易上当受骗呢?
  9. Extjs之EditorGridPanel的beforeedit事件参数
  10. 文献跟踪、文献订阅工具
  11. pathon中字典的基本用法
  12. 多个excel工作簿、工作表合并
  13. java 科学计数法位数_科学记数法android java
  14. Vue和Servlet搭配使用
  15. Git Bash 中的工具下载
  16. 懒逼 神经所 蒲慕明_【解放日报】中科院神经所所长蒲慕明:在祖国的工作是最大的贡献----中国科学院...
  17. 拉马努金的整数拆分全排列JAVA实现非递归
  18. sendmessage WM_PAINT 无效(6月19日)
  19. 博科5100交换机别名方式配置方法
  20. 2、叉叉助手逆向分析(上)

热门文章

  1. 计算机二级python考试题型和分值
  2. JavaScript的DOM之鼠标悬浮事件
  3. 利用谷歌的联邦学习框架Tensorflow Federated实现FedAvg(详细介绍)
  4. 如果五代时的后周皇帝柴荣没有英年早逝,历史将会有怎样的变化?
  5. win7 搜索 包括内容搜索设置
  6. python绘制训练结果曲线图和散点图、解决坐标刻度标签重复问题 、利用训练标准输出流绘制
  7. 22fall美硕EE转CS的暑期preparation
  8. cos php sdk v5使用,GitHub - lixiang1216/cos-php-sdk-v5: cos-php-sdk-v5
  9. 巨人史玉柱注资进军婚介行业 欲做第一媒老板“金婚配”
  10. 软件测试工程师面试题合集,建议收藏一波!