源码
public boolean contains(Object o) {//参数上转型
return indexOf(o) >= 0;//调用下面indexOf方法
}
public int indexOf(Object o) {
if (o == null) {//如果是null,则直接比较
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))//参数o调用equals方法与ArrayList集合各个元素进行比较
return i;
}
return -1;
}
泛型为String类型时
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//String类
ArrayList<String> names = new ArrayList<String>();
names.add("Jim");
System.out.println(names.contains("Jim"));//判断是否包含某个元素
}
}
代码分析:
- 泛型为String类型的ArrayList集合names首先调用contains方法,同时待比较的字符串"Jim"传到contains的参数上并进行上转型变成Object类型;
- 调用indexOf方法并将参数o传给indexOf方法的参数o;
- 判断不是null,进入else语句,再由参数o调用其equals方法,并将集合names中的每一个元素当做参数传给equals方法;
- 由于o为String类型的上转型对象,并且String类型中已经对equals进行了重写,所以在执行时会调用String类型中的equals方法,对字符串进行比较
- 最终如果找到了相同的字符串,则返回这个元素在ArrayList中的位置;否则返回-1;
- 再回到contains方法中,如果indexOf返回的是-1,则返回false;如果返回的是个非负整数,则返回这个数,即找到的相同元素(字符串)的位置。
泛型为包装类时
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//包装类
ArrayList<Integer> ages = new ArrayList<Integer>();
ages.add(12);
System.out.println(ages.contains(12));
}
}
代码分析:
- 泛型为Integer类型的ArrayList集合ages首先调用contains方法,同时待比较的整型12传到contains的参数上并进行上转型变成Object类型;
- 调用indexOf方法并将参数o传给indexOf方法的参数o;
- 判断不是null,进入else语句,再由参数o调用其equals方法,并将集合names中的每一个元素当做参数传给equals方法;
- 由于o为Integer类型的上转型对象,并且Integer类型中已经对equals进行了重写,所以在执行时会调用Integer类型中的equals方法,对整型进行比较
- 最终如果找到了相同值的整型,则返回这个元素在ArrayList中的位置;否则返回-1;
- 再回到contains方法中,如果indexOf返回的是-1,则返回false;如果返回的是个非负整数,则返回这个数,即找到的相同元素(整型)的位置。
泛型为自定义类型时
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//自定义类
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("111"));
System.out.println(students.contains(new Student("111")));
}
}
当Student类中未重写equals方法时:
public class Student {
String id;
public Student(String id) {//构造方法
this.id=id;
}
}
分析:
- 此时所有步骤和之前的大致一样,但调用equals方法时调用的是Object类中的equals方法
- 故会对二者的地址进行比较
- 显而易见二者的地址一定不同,即使二者学号相同,在我们看来是同一个学生,但由于存储的地址不同,最终返回的一定是false
- 从而造成了判断错误
当Student类中重写equals方法时:
public class Student {
String id;
public Student(String id) {
this.id=id;
}
@Override
public boolean equals(Object obj) {//重写equals方法
Student stu = (Student)obj;//将obj下转型,才能调用新增的id属性
return this.id.equals(stu.id);
}
}
分析:
- 此时重写了Object中的equals方法后,和String类与包装类一样,此时会调用Student类中重写的euqals方法
- 但由于equals方法中参数obj是上转型对象,无法掉用Student中新增的属性id,故要想比较id,必须先对其进行下转型
- 从而最终做到比较学生的id是否相同,而非地址
但当出现下面情况时会报错:
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//自定义类
ArrayList<Object> list = new ArrayList<Object>();//此时集合中可以放任何对象
list.add(new String("111"));
System.out.println(list.contains(new Student("111")));
}
}
- 由于是String对象调用contains方法,传入参数为Student对象
- 故当调用equals方法时,由Student对象调用其equals方法,并将String对象当做参数传给equals方法中
- 此时会将String对象强制转换为Student对象,于是报错
解决方法:使用instanceof关键字
public class Student {
String id;
public Student(String id) {
this.id=id;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Student) {//集合中可能存的未必是Student类创建的对象
Student stu = (Student)obj;
return this.id.equals(stu.id);
}
return false;
}
}
分析:
- 当集合中不是Student类创建的对象时,调用equals方法时便会直接返回false
- 从而解决了非Studnet类导致的报错问题