List的contains()⽅法详解以及重写equals()⽅法时需要注
意的地⽅
我们先来看看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⽅法,就必须要遵循以下规则:
下⾯举个例⼦:
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("ains(a)->" + ains(a));
System.out.println("ains(b)->" + ains(b));
list.clear();
list.add(b);
System.out.println("ains(a)->" + ains(a));
System.out.println("ains(b)->" + ains(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;
}
}
}
我们可以看到输出结果为:
表⾯上看来这样没有问题,但是如果我们只观察这个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;
}
equals不等于
public boolean equals(Object object){
if(object instanceof Person){
Person p = (Person) object;
Name() == null || name == null){
return false;
}
else{
return name.Name ());                  }
}
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()换成

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。