文章目录
源码
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
return indexOfRange(o, 0, size);
}
int indexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
1、String类型
ArrayList<String> names = new ArrayList<String>();
names.add("Jim");
System.out.println(names.contains("Jim"));
1.1、String类的分析方法
这个时候contains
方法会执行第一个源码中的位置,接着返回方法indexof
返回第二块源码位置,因为显然我们传入了一个值,“Jim”,这个值就是o,这时这个o显然不为null,所以代码会执行else
对应的代码块es[i]代表了集合中的所有元素,拿o也就是“Jim”于集合中的元素比较,这时候就会调用equasls方法。判断内容是否一样,所以返回true。
这里值得注意的是:如果contains传入的数据是String数据类型的对象,则contains方法本质上调用的是String对象中的equasls方法
2、包装类类型
ArrayList<Integer> age = new ArrayList<Integer>();
age.add(111);
System.out.println(age.contains(new Integer(111)));
2.1、包装类类型的分析方法
这个时候o就是12了,这个时候equals方法调用的是Integer中的equals方法,比较的值是否会相等,则明显12=12,返回true。
//Integer中的equals方法
public boolean equals(Object obj){
if(obj instanceof Integer){
return value == ((Integer)obj).intValue();
}
return false;
}
3、自定义类类型
先创建一个学生类
//学生类
public class Student {
private String id;
public Student(String id) {
this.id = id;
}
}
1 ArrayList<Student> students = new ArrayList<Student>();
2 students.add(new Student("1111"));
3 System.out.println(students.contains(new Student("1111")));
//Object类中equals方法
public boolean equals(Object obj){
return (this==obj);
}
- 分析
这个时候就不能进行判断这个集合中是否含有Student对象了,因为当时学生对象时,那么源码中的o就是Student对象,要进行时,需要调用equals方法,因为学生类中没有equasl方法,就会调用父类(Object)类中的equals方法,这时候比较的是地址是否相等,显然代码students.add(new Student("1111"));
和代码System.out.println(students.contains(new Student("1111")));
中是两个不同的对象(两个new),所以会返回false。
3.1、如何解决类类型中无法比较的问题?
- 重写equals方法
//重写equals方法后的学生类
public class Student {
private String id;
public Student(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
Student s = (Student) obj;
return this.id.equals(s.id);
}
}
当执行到equasl方法时,会调用我们在Student类中重写的equals方法,因为obj中是一个Object类型的变量,当我们进行学生类的比较时,我们应该先将obj下转型(强制类型转换),也就是我们要传入的变量,也就是代码students.add(new Student("1111"));
中的new Student对象。而在源码中,是o去调用equals方法,this指的就是o,返回内容是否相等,所以为true。
3.2、为什么重写之后需要加instanceof?
如果我们就像上面那样重写equasl方法,会发现一个问题,比如下面代码:
ArrayList<Object> list = new ArrayList<Object>();
list.add(new String("1111"));
System.out.println(list.contains(new Student("1111")));
发现执行上诉代码时,会出现异常,因为String类型是不可能强制类型转换成学生类的,所以我们要加上instanceof。
因为string类型没有可比性,所以如果传入的对象不是学生类,就直接返回false。
//改良后重写equals方法的学生类
public class Student {
private String id;
public Student(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Student) {
Student s = (Student) obj;
return this.id.equals(s.id);
}
return false;
}
}