分析ArrayList实现类中contains()方法底层代码

contains()方法是我们用于判断一个集合中是否含有某一元素的方法,那么作为ArrayList.util所提供的方法,它又是怎样实现的呢?本篇博客将深入contains()方法的底层代码,探究其实现原理。


情形一:参数存在

实例

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {
        ArrayList<String> nameList = new ArrayList();
        nameList.add("Tom");
	String name = "Tom";
	System.out.println(namelist.contains(name));
    }
}

上面代码段中,我们创建了集合nameList并添加了一个元素“Tom”,然后定义了一个字符串“Tom”.接下来,我们使用“Ctrl+单击contains方法”,打开contains所在的类,分析底层代码(见代码注释):

底层代码

public boolean contains(Object o) {//name为传入参数,o指向name对象,name为上转型对象
    return indexOf(o) >= 0;//调用下面的indexOf方法
}
public int indexOf(Object o) {//o同样指向name
    if (o == null) {//name指向的字符串不为null,if语句不执行
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)//在编译器中可看出,size是全局变量,其前面隐藏this(其实为i < this.size)用于获得集合nameList含有元素的个数
            if (o.equals(elementData[i]))//调用String类型中的equals方法将集合中的每个元素逐个与name相比是否相等
                return i;//相等则返回大于或等于零的值给上面contains方法最终返回true,判断结束
    }
    return -1;
}

情形二:参数不存在

实例

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {
        ArrayList<String> nameList = new ArrayList();
        nameList.add("Tom");
	String name = null;
	System.out.println(namelist.contains(name));
    }
}

这种情形下,name字符串为null,分析底层代码如下(见于代码注释):

底层代码

public boolean contains(Object o) {//name为传入参数,o指向name对象,name为上转型对象
    return indexOf(o) >= 0;//调用下面的indexOf方法
}
public int indexOf(Object o) {//o同样指向name
    if (o == null) {//name指向的字符串为null,执行if语句
        for (int i = 0; i < size; i++)//遍历集合中所有元素
            if (elementData[i]==null)//判断是否存在元素为null
                return i;//相等则返回大于或等于零的值给上面contains方法最终返回true,判断结束
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

注:使用equals()方法判断两对象是否相等的原理可参考:分析equals()方法底层代码

情形三:类型不同

实例

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {
        ArrayList<String> nameList = new ArrayList();
        nameList.add("Tom");
	Test test = new Test();
	System.out.println(namelist.contains(test));
    }
}

这种情况下我们定义了Test类型的test对象,若仍要判断泛型为String的集合是否包含test,结果是false,下面分析底层代码探究原理(见于代码注释):

底层代码

public boolean contains(Object o) {//test为传入参数,o指向test对象,test为上转型对象
    return indexOf(o) >= 0;//调用下面的indexOf方法
}
public int indexOf(Object o) {//o同样指向test
    if (o == null) {//test地址不为空,不执行if语句
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)//遍历集合中所有元素判断是否存在元素与test相同
            if (o.equals(elementData[i]))//使用equals方法判断是否相等,由于Test类没有重写equals方法,因此继承父类Object的equals方法(对比地址)
                return i;
    }
    return -1;//不相等,则返回-1值上面contains方法最终返回false,判断结束
}

情形四:判断自定义类型

实例

首先定义一个Student类并且定义其含参构造方法

public class Student {

    private String id;
    private String name;
    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

接下来我们创建Student类型对象,并向ArrayList集合中添加Student类型元素

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {
        ArrayList<Student> nameList = new ArrayList();
        nameList.add(new Student("001","Tom"));//向ArrayList集合中添加Student类型元素
	Student student = new Student("001","Jim");//创建Student类型对象
	System.out.println(namelist.contains(student));
    }
}

底层代码

public boolean contains(Object o) {//student为传入参数,o指向student对象,student为上转型对象
    return indexOf(o) >= 0;//调用下面的indexOf方法
}
public int indexOf(Object o) {//o同样指向student
    if (o == null) {//student地址不为空,不执行if语句
        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]))//o指向的对象student与nameList遍历出来的元素对比,由于Student没有重写equals()方法,所以用的是继承父类Object的equals方法(对比地址)
                return i;
    }
    return -1;//不相等,则返回-1值上面contains方法最终返回false,判断结束
}

思考

在上面“自定义类型”的实例中,即使创建的Student类型对象和向ArrayList集合中添加的Student类型元素字符串数值全部相同,调用contains方法的最终结果仍然是false原因就是因为,上面student对象和studentList元素的数据类型都是我们自定义的Student类型,因此在contains底层代码调用equals方法并非是我们常用的String类当中的equals方法,而是Student类型继承父类Object中的equals方法,而Object中的equals方法中仅仅判断两对象地址是否相等!

那么,如果在Student类中重写equals方法,那么就可以达到自定义判断的效果,改动如下:

public class Student {

    private String id;
    private String name;
    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }
//  @Override
//  public boolean equals(Object obj) {
//      return super.equals(obj);
//  }
//上面是默认继承Object的equals方法    
    @Override
    public boolean equals(Object obj) {
        Student student = (Student)obj;//将我们前面用的元素(作为参数传递给Object类型的变量obj称为上转型)下转型
	return this.id.equals(student.id);//this代表调用这个方法的对象o(指向name);id是String类型,调用String类型中的equals方法判断是否与元素的id相同
    }
}

上面代码通过重写equals()方法实现了比较自定义类Student的id属性,当然我们也可以重写equals使其达到比较Student类的name属性。


 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值