List的contains()方法详解以及重写equals()方法时需要注意的地方

文章摘抄自http://blog.csdn.net/javazejian/article/details/51348320

我们先来看看contains方法内部实现

@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
进入indexof方法

@Override
public int indexOf(Object o) {
E[] a = this.a;
//当o是null的时候也会返回集合中null的位置
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
//indexOf(),实际调用的是传入的contains(Object o)的实参的equals方法
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
一般情况下我们没有重写equals()方法,这时就会调用继承自object类的equals()方法,而我们都知道object的equals()方法,实际就是 ==,如下

public boolean equals(Object obj) {
return (this == obj);
}
比较的是是否为同一个对象。所以再调用contains()方法时,实际就是看是否是同一个对象,但是如果重写了contains(Object o)中形参的equals()方法,那么就会产生不同的效果。

而大家都知道,如果重写equals方法,就必须要遵循以下规则:

自反性。对于任何非null的引用值x,x.equals(x)应返回true。

对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。

传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。

一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。

对于任何非空引用值x,x.equal(null)应返回false。

下面举个例子:

import java.util.ArrayList;
import java.util.List;

public class AbnormalResult {
public static void main(String[] args) {
List list = new ArrayList();
A a = new A();
B b = new B();
list.add(a);
System.out.println(“list.contains(a)->” + list.contains(a));
System.out.println(“list.contains(b)->” + list.contains(b));
list.clear();
list.add(b);
System.out.println(“list.contains(a)->” + list.contains(a));
System.out.println(“list.contains(b)->” + list.contains(b));
}
//我们通过静态内部类来精简代码,可以看到我们都重写了List中插入元素的equals方法,这样在调用contains方法时,就会按照我们的equals方法进行操作
static class A {
@Override
public boolean equals(Object obj) {
return obj instanceof A;
}
}
static class B extends A {
@Override
public boolean equals(Object obj) {
return obj instanceof B;
}
}
}
我们可以看到输出结果为:

list.contains(a)->true

list.contains(b)->false

list.contains(a)->true

list.contains(b)->true

表面上看来这样没有问题,但是如果我们只观察这个equals方法就会发现,它违反了重写equals()方法的对称性原则,因为上面调用的四次contains()方法,实际调用的equals()方法如下所示:

①a.equals(a)->true;//因为我们只插入了一个元素,所以就是在跟插入的这个元素比较,下同

②b.equals(a)->false;

③a.equals(b)->true;

④b.equals(b)->true;

可以看到②③的比较的结果是不同的,这就违反了对称性原则。所以,当你打算重写contains()方法的equals()规则时,一定要检查清楚,是否符合以上原则,验证后再进行使用。

上面的代码改为如下,就会都返回true

static class B extends A{
@Override
public boolean equals(Object obj) {
if(obj instanceof B){
return true;
}
return super.equals(obj);
}
}
但实际上这种方法又会违背传递性原则,比如下面一个例子

//父类
public class Person {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name){
this.name = name;
}
public boolean equals(Object object){
if(object instanceof Person){
Person p = (Person) object;
if(p.getName() == null || name == null){
return false;
}
else{
return name.equalsIgnoreCase(p.getName ());
}
}
return false;
}
}
//子类
public class Employee extends Person{
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Employee(String name,int id){
super(name);
this.id = id;
}
/**
* 重写equals()方法
*/
public boolean equals(Object object){
if(object instanceof Employee){
Employee e = (Employee) object;
return super.equals(object) && e.getId() == id;
}
return super.equals(object);
}
//测试代码
public class Test {
public static void main(String[] args) {
Employee e1 = new Employee(“chenssy”, 23);
Employee e2 = new Employee(“chenssy”, 24);
Person p1 = new Person(“chenssy”);
System.out.println(p1.equals(e1));
System.out.println(p1.equals(e2));
System.out.println(e1.equals(e2));
}
}
根据传递性原则,e1.equals(e2)也应该返回true,但实际结果是false,对于那 e1!=e2 我们非常容易理解,因为他们不仅需要比较 name,还需要比较 ID。但是 p1 即等于 e1 也等于 e2,这是非常奇怪的,因为 e1、e2 明明是两个不同的类,但为什么会出现这个情况?首先 p1.equals(e1),是调用 p1 的 equals 方法,该方法使用 instanceof 关键字来检查 e1 是否为 Person 类,这里我们再看看 instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系,肯定会返回 true 了,而两者 name 又相同,所以结果肯定是 true。所以出现上面的情况就是使用了关键字 instanceof,这是非常容易导致我们“钻牛角尖”。故在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof(除非子类拥有统一的语义)

因为getclass()方法判断的是是否为同一个类,而instanceof()判断是否有继承关系,如果上面equals(),里面的instanceof()换成object.getClass()==Employee.class,就不会出现上述问题,因为都返回false。

作者:shenqueying
来源:CSDN
原文:https://blog.csdn.net/shenqueying/article/details/80073140
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/sjn0815/article/details/91956269