contains()方法可以用来判断集合中是否含有某元素,若有则返回true,没有则返回false。
contains()源码:
public boolean contains(Object o) { //此处的o即为contains方法中的参数对象
return indexOf(o) >= 0;//如果数值>=0,则返回true
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)//elementDate[i]:去遍历集合中的元素 一直到找到集合中为null的元素为止(在for循环满足的条件下)
return i;
} else {// o != null时
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))//o(即contains方法中的参数对象)调用相应的equals()方法
return i;
}
return -1;
}
上面o(即contains方法中的参数对象)调用的什么样的equals()方法取决于o是什么类型:
contains()方法中的参数类型 | 调用的equals()方法 |
---|---|
String类型 | String对象中的equals()方法 |
基本数据类型的包装类 | 包装类中的equals()方法 |
类类型 | 类类型中的equals()方法 |
String类型:
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<String>();
names.add("刘一");
names.add("陈二");
System.out.println(names.contains("陈二"));
}
}
这时输出的结果肯定是:true
让我们来分析一下其中的判断过程是怎样的:
- 当执行到
names.contains("陈二")
时,调用contains()方法,(请看contains()源码,)其中"陈二"赋给o,o即为String类, - 接着再调用indexOf()方法,因为o != null,所以进入else{}语句里,o去调用String类的equals()方法(先比较地址,再比较每个字符,有一样相同即返回true),与集合中的元素进行比较,一旦找到相同的(即equals()方法返回true),则返回此时对应的i,
- 跳转回contains()方法中,因此时i>=0,所以返回true。
- 补充一点:若o=null(即
names.contains("")
),则执行 if 语句,接着通过for循环去判断集合中是否有值为null的元素,若有,则返回i(此时肯定有i>=0),再返回true,若遍历完整个集合也没找到值为null的元素,则跳出if语句,执行最后一条语句return -1;
,所以最后返回false。
包装类:
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Integer> ages = new ArrayList<Integer>();
ages.add(20);
ages.add(12);
System.out.println(ages.contains(12));
}
}
输出的结果为:true
我们再来分析一下:
ages.contains(12)
里的12看似是int类型,实则进行了隐形转换,相当于ages.contains(new Integer(12))
- 当执行到
ages.contains(12)
时,调用contains()方法,(请看contains()源码),12赋给了o,因此o为Integer类型, - 再跳转到indexOf()方法中,因为o!=null,进入else()语句中,o调用Integer中的equals()方法,而Integer中的equals()方法比较的是数值是否相等,因为o的数值与集合中的一个元素相等,所以equal()方法返回true,进入if语句,返回i,(当然喽,若一直到循环终止也没有哪个元素的值与o的值相等,则跳出else语句,执行
return -1;
最终返回false) - 跳转到contains中因此是i>=0,所以返回true
- 补充一点:若contains()中的参数为空,即为null,与上面String类型中的第四点是一样的。
自定义类类型(分为三点):
- 未重写equals()方法:
先定义一个Student类:
public class Student {
private String id;//学号
public Student(String id){
this.id = id;
}
}
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("2019"));
students.add(new Student("2020"));
System.out.println(students.contains(new Student("2020")));
}
}
这时候返回的是:false
为什么呢?集合中有学号为"2020"的学生呀,为什么不返回true呢?让我们来深入分析一下:
- 执行到
students.contains(new Student("2020"))
时,跳转到contains()方法,(请看contains()源码),创建一个Student对象"2020"赋值给o,因此o为Student类, - 接着跳转到indexOf()方法中,因为o!=null,进入else语句里,o本来要调用Student中的equals()方法的,但Student类中没有重写equals()方法,因此去调用Student父类(Object)中的equals()方法,而Object中的equals()方法比较的是地址,我们知道每创建一个对象,都会新开辟一个空间,即每创建一个对象都会有一个新的地址,因此o的地址与集合中的每个元素的地址都不一样,所以equals()方法返回false,if语句条件不满足,跳出else()语句,执行
return -1;
, - 跳转回contains()方法里,最终返回false。
- 补充一点:当o=null时,也与Strig类型中的第4点一样。
- 重写了equal()方法:
我们知道学生的学号是唯一的,所以上述查到学号为"2020"的学生时应返回true,那么怎么做呢?这时就要重写父类中的equals()方法,使其满足我们现实生活中的逻辑,
public class Student {
private String id;//学号
public Student(String id){
this.id = id;
}
public boolean equals(Object obj) {//obj就是集合中的每一个元素
Student s = (Student)obj;//需要下转型,因为上一行obj上转型了
return this.id.equals(s.id);//谁调用equals()方法,this就指谁,这里是o(即contains方法中的参数对象)
}
}
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("2019"));
students.add(new Student("2020"));
System.out.println(students.contains(new Student("2020")));
}
}
这次返回的结果就是:true了,让我们再来分析一下:
- 当代码执行到
students.contains(new Student("2020"))
时,跳转到contains()方法,(请看contains()源码),创建的一个新的对象"2020"赋给o,因此o属于Student类, - 再调用indexOf()方法,因为o!=null,所以进入else语句中,然后o去调用Student中的equals()方法,而我们这里定义的这个equals()方法也是先比较地址,再比较字符,其中一项一样,即返回true,所以条件满足,进入if语句,执行
return i;
, - 再跳转回contains()方法中,此时i>=0,所以最终返回true
- 补充一点:跟上面第4点一样
- 在重写equals()中使用 instanceof:
仔细看上面的代码是有bug的,因为一个集合只能放泛型规定的东西,若此时用contains()方法来辨别一个东西是否属于该集合(即是否有一个元素与该东西相同),若该东西不属于泛型之内,则代码会报错,而不是返回false,这时我们就需要instanceof来辨别出要判断的东西是否属于某一类东西,从而让代码更加人性化:
其中 instanceof 的作用是:测试它左边的对象是不是属于右边的类的实例,若是返回true,若不是则返回false
public class Student {
private String id;
public Student(String id){
this.id = id;
}
public boolean equals(Object obj) {
if(obj instanceof Student) {//先判断一下obj中的元素是否属于Student
Student s = (Student)obj;
return this.id.equals(s.id);
}
return false;
}
}
注意:此时的泛型变为<Object>,不再是<Student>
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<Object>();
list.add(new String("123"));
list.add(new Student("2020"));
System.out.println(list.contains(new Student("2020")));
}
}
这时返回的结果就是true了,若没有instanceof先来判断一下,则代码会报错,因为"123"与Student无任何关系,不能进行强制类型转换(也就是下转型)所以会报错,让我们来具体分析一下:
- 代码执行到
list.contains(new Student("2020"))
时,调用contains()方法,(请看contains()源码), 新创建的Student对象"2020"会赋给o,因此o为Student类, - 再调用indexOf()方法,因为o!=null,所以进入else语句中,o调用Student中的equals()方法,然后执行
obj instanceof Student
,判断obj(即集合中的各个元素,直到找到或循环停止为止)中的元素是否属于Student类,再做进一步判定,若不是Student类,则直接执行return false;
,若属于Student类,再判定其地址、字符是否与obj中的某个元素相同,若有一样相同,则返回true,若都不同,则返回false, - 最后再返回到contains()方法中,输出相应的boolean逻辑值,此例最后返回的是true
- 补充一点:若o=null,则与上述相同。