Java中Set的contains()方法
2016-04-15 10:27
645 查看
新手对Set中contains()方法的疑惑
[java] view
plain copy
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!");
}
}
}
上述代码的输出为:
[plain] view
plain copy
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,代码如下所示:
[java] view
plain copy
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类修改为如下代码,能实现我们的目标吗?
[java] view
plain copy
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()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。
解决办法
[java] view
plain copy
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是不变的.否则会出现如下的这种困惑:
[java] view
plain copy
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
}
}
[java] view
plain copy
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!");
}
}
}
上述代码的输出为:
[plain] view
plain copy
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,代码如下所示:
[java] view
plain copy
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类修改为如下代码,能实现我们的目标吗?
[java] view
plain copy
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()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。
解决办法
[java] view
plain copy
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是不变的.否则会出现如下的这种困惑:
[java] view
plain copy
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
}
}
相关文章推荐
- 在类中创建本类对象(与枚举类似)
- Java synchronized详解
- java基本常识
- 对上次“对字符串进行简单的字符数字统计 探索java中的List功能 ”程序,面向对象的改进
- Java反射setAccessible()方法
- java 字符串转成 json 数组并且遍历
- eclipse运行maven install的时候,提示找不到JDK中的rt.jar包下的程序
- Java多线程(3) 线程之间通信
- Spring Dao使用自动注入配置错误小记
- JVM(7)--java内置工具使用(2)
- run scala with java use spring mvc(java项目加入 scala)
- JVM参数配置大全
- java.net.SocketException四大异常解决方案
- java中对map的字典序排序
- Java类型Float&&Double
- java.lang.NoClassDefFoundError问题的解决办法
- JVM调优简述
- java实现DOM4J解析XML
- 信用卡必须是 13-16 位的整数串,它必须通过 Luhn 算法来验证通过才是合法的卡号。 Luhn 算法校验的过程是: • 从卡号最后一位数字开始,逆向将奇数位(1、3、5 等)相加。 • 从卡
- 验证码的实现