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<A> list = new ArrayList<A>();  
        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。

  • 17
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值