Java中Set的contains()方法
Java中Set的contains()方法 —— hashCode与equals方法的约定及重写原则
翻译人员: 铁锚
翻译时间: 2013年11月5日
原文链接: Java hashCode() and equals() Contract for the contains(Object o) Method of Set
本文主要讨论 集合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;
}
@Override
public int hashCode() {
// 请考虑是否值得这么做,因为此时name是会变的.
return name.length();
// 推荐让name不可改变
}
@Override
public 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
}
}
参考文章:
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
Java中Set的contains()方法的更多相关文章
- java中substring的使用方法
java中substring的使用方法 str=str.substring(int beginIndex);截取掉str从首字母起长度为beginIndex的字符串,将剩余字符串赋值给str: str ...
- [java,2017-05-16] java中清空StringBuffer的方法以及耗费时间比较
java中清空StringBuffer的方法,我能想到的有4种: 1. buffer.setLength(0); 设置长度为0 2. buffer.delete(0, buffer.length() ...
- java中BorderLayout的使用方法
相关设置: 使用BorderLayout布局上下左右中布局5个按键,单击中间的那个按键时就关闭窗口 代码: /**** *java中BorderLayout的使用方法 * 使用BorderLayout ...
- 【Java】Java中常用的String方法
本文转载于:java中常用的String方法 1 length()字符串的长度 String a = "Hello Word!"; System.out.println(a.len ...
- Java中Set的contains()方法——hashCode与equals方法的约定及重写原则
转自:http://blog.csdn.net/renfufei/article/details/14163329 翻译人员: 铁锚 翻译时间: 2013年11月5日 原文链接: Java hashC ...
- java中equals和hashCode方法随笔二
前几天看了篇关于java中equals和hashCode方法的解析 1.Object类中的equals方法和hashCode方法. Object类中的equals和hashCode方法简单明了,所有的 ...
- java中static变量和方法的总结
转自:http://blog.csdn.net/haobo920/article/details/5921621 java中static变量和方法的总结 java中一切皆是对象 一个类中对象的定义一般 ...
- Java中wait和sleep方法的区别
1.两者的区别 这两个方法来自不同的类分别是Thread和Object 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法(锁代码块和方法锁). wait ...
- java中File的delete()方法删除文件失败的原因
java中File的delete()方法删除文件失败的原因 学习了:http://hujinfan.iteye.com/blog/1266387 的确是忘记关闭了: 引用原文膜拜一下: 一般来说 ja ...
随机推荐
- OpenCV3.1.0中调用MHI(Motion History Images, 运动历史图像)
写在前边: OpenCV3.0+要想使用MHI,就要现安装扩展模块opencv_contrib.安装方法见:ubuntu 14.04 64位 安装Opencv3.1.0 (包含opencv_contr ...
- CNN在中文文本分类的应用
深度学习近一段时间以来在图像处理和NLP任务上都取得了不俗的成绩.通常,图像处理的任务是借助CNN来完成的,其特有的卷积.池化结构能够提取图像中各种不同程度的纹理.结构,并最终结合全连接网络实现信息的 ...
- ngx.ctx
https://github.com/openresty/lua-nginx-module#ngxctx 要点 生命周期和请求一致 每个请求的ngx.ctx是相互独立的,包括ngx.location. ...
- MongoDB实用教程
---------------------------------------------------------------------------------------------------- ...
- Detailed Item Cost Report (XML) timed out waiting for the Output Post-processor to finish
In this Document Symptoms Cause Solution References APPLIES TO: Oracle Cost Management - Ver ...
- 解决linux删除文件后空间没有释放问题
linux删除文件后沒有释放空间 今天发现一台服务器的home空间满了,于是要清空没用的文件,当我删除文件后,发现可用空间沒有变化 os:centos4.7 现象: 发现当前磁盘空间使用情况: [ro ...
- NewSQL数据库VoltDB特性简介
VoltDB是一个革命性的新型数据库产品,被称作NewSQL数据库.它基于H-Store,号称比当前数据库产品的吞吐量高45倍,同时又具有很高的扩展性.它的特性主要有以下几点: Ø 高吞吐.低延迟: ...
- Cocos2D在新版Swift中常量枚举值引用代码的修改
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我们知道在SpriteBuilder中是无法直接给一个CCB文 ...
- DBoW2应用
图像对应的bag-of-words向量\(v_t\) 假设词典总共有\(W\)个单词,那么每一幅图像能够用一个\(W\)维的向量表示 \((t_1, t_2, t_3, ..., t_W)\)其中 \ ...
- Erlang edoc 多级目录出错
Erlang edoc 多级目录出错使用rebar doc来生成项目文档.但是当erl源文件目录src下建立子目录,并新建erlang文件后,就无法生成文档. 例如,新建 src/tttt/, 并添加 ...