List和Set的contains()方法实现原理

本文探讨了Java中Set和List的contains()方法的工作原理,特别是如何通过正确重写equals和hashCode方法确保Set能够准确判断元素是否存在。

新手对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。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值