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

您所在的位置:网站首页 java重载equals List的contains()方法详解以及重写equals()方法时需要注意的地方

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

#List的contains()方法详解以及重写equals()方法时需要注意的地方| 来源: 网络整理| 查看: 265

文章摘抄自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。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3