详述ArrayList类中的contains()方法及原理

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
让我们来分析一下其中的判断过程是怎样的:

  1. 当执行到names.contains("陈二")时,调用contains()方法,(请看contains()源码,)其中"陈二"赋给o,o即为String类,
  2. 接着再调用indexOf()方法,因为o != null,所以进入else{}语句里,o去调用String类的equals()方法(先比较地址,再比较每个字符,有一样相同即返回true),与集合中的元素进行比较,一旦找到相同的(即equals()方法返回true),则返回此时对应的i,
  3. 跳转回contains()方法中,因此时i>=0,所以返回true。
  4. 补充一点:若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))

  1. 当执行到ages.contains(12)时,调用contains()方法,(请看contains()源码),12赋给了o,因此o为Integer类型,
  2. 再跳转到indexOf()方法中,因为o!=null,进入else()语句中,o调用Integer中的equals()方法,而Integer中的equals()方法比较的是数值是否相等,因为o的数值与集合中的一个元素相等,所以equal()方法返回true,进入if语句,返回i,(当然喽,若一直到循环终止也没有哪个元素的值与o的值相等,则跳出else语句,执行return -1;最终返回false)
  3. 跳转到contains中因此是i>=0,所以返回true
  4. 补充一点:若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呢?让我们来深入分析一下:

  1. 执行到students.contains(new Student("2020"))时,跳转到contains()方法,(请看contains()源码),创建一个Student对象"2020"赋值给o,因此o为Student类,
  2. 接着跳转到indexOf()方法中,因为o!=null,进入else语句里,o本来要调用Student中的equals()方法的,但Student类中没有重写equals()方法,因此去调用Student父类(Object)中的equals()方法,而Object中的equals()方法比较的是地址,我们知道每创建一个对象,都会新开辟一个空间,即每创建一个对象都会有一个新的地址,因此o的地址与集合中的每个元素的地址都不一样,所以equals()方法返回false,if语句条件不满足,跳出else()语句,执行return -1;
  3. 跳转回contains()方法里,最终返回false。
  4. 补充一点:当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了,让我们再来分析一下:

  1. 当代码执行到students.contains(new Student("2020"))时,跳转到contains()方法,(请看contains()源码),创建的一个新的对象"2020"赋给o,因此o属于Student类,
  2. 再调用indexOf()方法,因为o!=null,所以进入else语句中,然后o去调用Student中的equals()方法,而我们这里定义的这个equals()方法也是先比较地址,再比较字符,其中一项一样,即返回true,所以条件满足,进入if语句,执行return i;
  3. 再跳转回contains()方法中,此时i>=0,所以最终返回true
  4. 补充一点:跟上面第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无任何关系,不能进行强制类型转换(也就是下转型)所以会报错,让我们来具体分析一下:

  1. 代码执行到list.contains(new Student("2020"))时,调用contains()方法,(请看contains()源码), 新创建的Student对象"2020"会赋给o,因此o为Student类,
  2. 再调用indexOf()方法,因为o!=null,所以进入else语句中,o调用Student中的equals()方法,然后执行obj instanceof Student,判断obj(即集合中的各个元素,直到找到或循环停止为止)中的元素是否属于Student类,再做进一步判定,若不是Student类,则直接执行return false;,若属于Student类,再判定其地址、字符是否与obj中的某个元素相同,若有一样相同,则返回true,若都不同,则返回false,
  3. 最后再返回到contains()方法中,输出相应的boolean逻辑值,此例最后返回的是true
  4. 补充一点:若o=null,则与上述相同。
发布了34 篇原创文章 · 获赞 8 · 访问量 695

猜你喜欢

转载自blog.csdn.net/weixin_45720626/article/details/105545859
今日推荐