新手对Set中contains()方法的疑惑
class Dog {
int id;
String color;
public Dog(int id, String color) {
this.id = id;
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class TestContains {
public static void main(String[] args) {
Set<Dog> dogSet = new HashSet<Dog>();
dogSet.add(new Dog(1, "white"));
dogSet.add(new Dog(2, "white"));
System.out.println(dogSet.contains(new Dog(1, "white")));
List<Dog> dogList = new ArrayList<Dog>();
dogList.add(new Dog(1, "white"));
dogList.add(new Dog(2, "white"));
System.out.println(dogList.contains(new Dog(1, "white")));
}
}
输出结果为:false false
Set的contains(Object o) 方法详解
Java的API文档指出:当且仅当本Set包含一个元素 e,并且满足(o==null ? e==null : o.equals(e))条件时,contains()方法才返回true。因此 contains()方法必定使用equals方法来检查是否相等。
需要注意的是:Set 中是可以包含 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 {
int id;
String color;
public Dog(int id, String color) {
this.id = id;
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
/**
* 重写equals方法,最佳实践就是如下这种判断顺序
*/
public boolean equals(Object obj) {
if (!(obj instanceof Dog))
return false;
if (obj == this)
return true;
Dog dog = (Dog) obj;
if (this.id == dog.getId() && this.color.equals(dog.getColor())) {
return true;
}
return false;
}
}
输出结果还是:false true
List可以判断,Set还是不能判断
问题的关键在于 Java中hashCode与equals方法的紧密联系。 hashCode() 是Object类定义的另一个基础方法。
如果两个对象相等(使用equals()方法),那么必须拥有相同的哈希码(使用hashCode()方法)。
即使两个对象有相同的哈希值(hash code),他们不一定相等。意思就是:多个不同的对象,可以返回同一个hash值。
hashCode()的默认实现是为不同的对象返回不同的整数。有一个设计原则是,hashCode对于同一个对象,不管内部怎么改变,应该都返回相同的整数值。
在上面的例子中,因为未定义自己的hashCode()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。
解决办法
再重写hashCode()方法
class Dog {
int id;
String color;
public Dog(int id, String color) {
this.id = id;
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
/**
* 重写equals方法,最佳实践就是如下这种判断顺序
*/
public boolean equals(Object obj) {
if (!(obj instanceof Dog))
return false;
if (obj == this)
return true;
Dog dog = (Dog) obj;
if (this.id == dog.getId() && this.color.equals(dog.getColor())) {
return true;
}
return false;
}
/**
* 重写hashCode方法
*/
public int hashCode() {
String hashCodeMsg = id + "_" + color;
return hashCodeMsg.hashCode();
}
}
public class TestContains {
public static void main(String[] args) {
Set<Dog> dogSet = new HashSet<Dog>();
dogSet.add(new Dog(1, "white"));
dogSet.add(new Dog(2, "white"));
System.out.println(dogSet.contains(new Dog(1, "white")));
List<Dog> dogList = new ArrayList<Dog>();
dogList.add(new Dog(1, "white"));
dogList.add(new Dog(2, "white"));
System.out.println(dogList.contains(new Dog(1, "white")));
HashSet<Dog> a = new HashSet<Dog>();
a.add(null);
if (a.contains(null)) {
System.out.println("true");
}
}
}
输出结果:true true
总结
1、HashSet的contains返回true,当且仅当equals返回true并且hashCode返回相等的值 ;
2、list.contains(o),系统会对list中的每个元素e调用o.equals(e)方法,假如list中有n个元素,那么会调用n次o.equals(e),只要有一次o.equals(e)返回了true,那么list.contains(o)返回true,否则返回false。
本文探讨了Java中Set和List的contains()方法的工作原理,特别是如何通过正确重写equals和hashCode方法确保Set能够准确判断元素是否存在。

8745

被折叠的 条评论
为什么被折叠?



