在ArrayList类集合中很常用的方法之一就是contains()方法,它可以判断一个集合中是否含有指定元素,知其然更要知其所以然,知道了它的作用后为了以后能更好的使用它,我们以举几个示例来来分析一下底层代码。
1.第一个示例
package contains;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList();
list.add("Tom");
System.out.println(list.contains("Tom"));
}
}
按住Ctrl键点击contains方法进入底层代码查看到如下源代码:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == 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]))
return i;
}
return -1;
}
第1行:当list调用contains方法时,传入变量name,用Object类型o存放String类对象name,所以这是一个对象上转型的过程
第2行:返回值时调用了下面的indexOf方法。
第5行:调用该方法时将上一个方法中的Object类型变量o传入该方法中,此时该方法中的o还是一个指向String类型对象name的上转型对象。
第6行:判断该上转型对象是否为null,该例中name显然不为null,所以返回值为false,执行else代码块。
第11行:进入for循环遍历集合中的元素,遍历次数就是集合中元素的个数。
第12行:该处的使用是一个多态,表层上调用的是Object类中的equals方法,实则调用的是String类中的equals方法,将指向的对象name与遍历出来的集合中的每一个元素进行比较,该例中当遍历到第一个元素时即返回true进入if代码块返回true然后结束方法。
2.第二个示例
该例中将contains方法中传入的参数name变成null:
package contains;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList();
list.add("Tom");
String name=null;
System.out.println(list.contains(name));
}
}
这时我们再来分析一下底层代码:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == 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]))
return i;
}
return -1;
}
第1行:当list调用contains方法时,传入变量name,用Object类型o存放String类对象name,所以这是一个对象上转型的过程
第2行:返回值时调用了下面的indexOf方法。
第5行:调用该方法时将上一个方法中的Object类型变量o传入该方法中,此时该方法中的o还是一个指向String类型对象name的上转型对象。
第6行:判断该上转型对象是否为null,该例中name变成了null,所以返回值为true,执行if代码块。
第7行:进入for循环遍历集合中的元素,遍历次数就是集合中元素的个数。
第8行:该语句对集合中遍历的每一个元素判断是否为null,该例中集合中没有null元素,所以这里并没有进入if代码块,跳出了for循环,返回了-1。
3.第三个示例
该例中创建了一个Test类对象test,调用contains方法时将test传入方法,这时我们再来分析一下底层代码的执行情况:
package contains;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList();
list.add("Tom");
Test test=new Test();
System.out.println(list.contains(test));
}
}
还是分析下面这段底层代码:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == 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]))
return i;
}
return -1;
}
第1行:当list调用contains方法时,传入变量test,用Object类型o存放Test类对象test,所以这是一个对象上转型的过程
第2行:返回值时调用了下面的indexOf方法。
第5行:调用该方法时将上一个方法中的Object类型变量o传入该方法中,此时该方法中的o还是一个指向Test类型对象test的上转型对象。
第6行:判断该上转型对象是否为null,该例中test中存的是堆中的地址,不为空,所以返回值为false,执行else代码块。
第11行:进入for循环遍历集合中的元素,遍历次数就是集合中元素的个数。
第12行:该处正常来说应该是一个多态的调用,因为Object类中指向的是Test类的对象test,所以表层调用Object类的方法实则应该调用Test类中的equals方法,但是Test类中并没有equals方法,所以调用的相当于还是Object类中的equals方法。
下面是Object类中equals方法的源代码:
public boolean equals(Object obj) {
return (this == obj);
}
可以看出,该方法仅仅是判断二者的地址是否相同,由于二者不是同一类,所以在集合中一定没有同样地址的元素,一定不会执行到该if代码块。
第15行:最后返回-1
四.思考:
第三个示例是由于Test类中没有重写equals方法,所以调用的Object类中的equals方法,那么如果我们在Test类中重写一个equals方法,那就意味着可以调用自己重写的方法:
package contains;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList();
list.add("Tom");
Test test=new Test();
System.out.println(list.contains(test));
}
@Override
public boolean equals(Object obj) {
System.out.println("111");
return super.equals(obj);
}
}