Collection集合中的contains():
boolean contains(Object o) 判断 集合中 是否 包含指定的元素
contains方法源码:jdk-11.0.4
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;
}
对于String类型的ArrayList集合:
ArrayList<String> names = new ArrayList<>();
names.add("jim");
boolean b = names.contains("jim");
System.out.println(b);//true
此时我们调用contains方法时,会调用String中的equals方法,对其进行比较,返回值为boolean型,源码如下
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
它首先判断的是两个对象的引用是否指向同一内存地址,是的话就返回true ,否则继续往下走,往下走时,判断是否为String类的对象,然后比较两个字符串的长度和内存,当都相等的时候就返回true ,否则返回false。
如果我们对包装类进行使用contains方法,如下:
ArrayList<Integer> ages = new ArrayList<Integer>();
ages.add(12);
System.out.println(ages.contains(new Integer(12)));
此时我们查看源码会发现,其调用的是Integer中的equals方法,也就是说,各个包装类都对equals方法进行了重写
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
此时比较的是值是否相等。
但是如果我们使用自定义类类型的泛型调用contains方法时,我们会发现,即使两个内容一样,返回值依然是false:
我们首先创建一个Student自定义类:
public class Student {
private String id;
public Student(String id) {
this.id = id;
}
}
此时如果我们使用contains方法:
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("111"));
// System.out.println(students.getClass());//class java.util.ArrayList
System.out.println(students.contains(new Student("111")));//false
我们发现此时返回值是false,但是我们知道这两个学生的id是一样的,之所以会返回false是因为此时我们并没有重写equals方法,当程序执行到equals方法时,默认调用的是Object类中的equals方法,源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
此时我们发现,比较的是地址值,那么很明显,两个对象的地址值是一定不相同的,所以会返回false,为了解决这个问题,我们需要对其中的equals方法进行重写,重写后Student类如下:
public class Student {
private String id;
public Student(String id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
}
此时如果我们再次运行程序,返回的就是true,其中我们需要注意的是,必须要对传进来的参数进行比较,必须是Student类型的,否则强制类型转换就会出现问题,也可以使用instance of方法进行判断是否属于Student类(即自定义类),如果是,那么进行强制类型转换,然后对其中的内容进行比较,需要注意的是,重写后的equals方法中,
return Objects.equals(id, student.id);
此时调用的并非是自身,而是其他的equals方法:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
从而可以对其进行判断内容是否相同
如果此时我们传入一个并非是Student类型的参数,很明显会返回false
if (o == null || getClass() != o.getClass()) return false;
演示如下:
ArrayList<Object> list = new ArrayList<>();
System.out.println(list.getClass());
list.add(new String("111"));
System.out.println(list.contains(new Student("111")));//false