深度解析Contains底层代码

深度解析Contains底层代码

在分析contains之前,我们先看一下==和equals区别,

1、==和equals区别

String str1="Jim";
String str2 ="Jim";
System.out.println(str1==str2);

==:基本数据类型比较值是否相同;引用类型比较地址是否相同

String str3="Jim";
String str4 =new String("Jim");
System.out.println(str3.equals(str4));

equals:Object类中equals实质就是比较地址是否相同;一些数据类型在引用上重写了equals(),String类中equals实质是比较字符转内容后是否相同。

2.contains底层源码分析

1>先上一段代码来调用contains。

List<String> name= new ArrayList<>();
    name.add("Jimmy");
    System.out.println(name.contains("Tonny"));

2>按着键盘Ctrl+鼠标左键单击进入ArrayList数组里的contains方法。这是我们会看到如下内容,其object里面的参数o为我们要比较的对象Tonny,对Tonny进行了上转型操作,返回值为判断indexof(Tonny) >= 0。

public boolean contains(Object o) {//Tonny为传入参数,o指向Tonny对象,Tonny为上转型对象
    return indexOf(o) >= 0;//调用下面的indexOf方法
}

3>接着Ctel+左键点击indexof,进入indexof方法,该方法并未做什么操作,只是将参数封装调入到另一个方法indexOfRange取进行字符串截取比较。点击进去indexOfRange。

public int indexOf(Object o) {
    return indexOfRange(o, 0, size);
}

4>关键点:字符串比较。

    int indexOfRange(Object o, int start, int end) {
    //Tonny为传入参数,start为该数组的0位,end为末尾。
        Object[] es = elementData;//将要进行判断的数组列表
        if (o == null) {//Tonny地址不为空,不执行if语句
            for (int i = start; i < end; i++) {
                if (es[i] == null) {
                    return i;
                }
            }
        } else {//遍历集合中所有元素判断是否存在元素与test相同
            for (int i = start; i < end; i++) {
                if (o.equals(es[i])) {//使用equals方法判断是否相等
                    return i;相等,则返回该值所在位置坐标,上面contains方法最终返回true。
                }
            }
        }
        return -1;//不相等,则返回-1值上面contains方法最终返回false。
    }

 

3、contains代码实用场景

1。String类型比较
两者参数相同,类型相同,最后返回值为true。如果类型不同||参数不同则返回结果为false。
List<String> names= new ArrayList<>();
    names.add("Jimmy");
    String name="Jimmy"
    System.out.println(names.contains("Jimmy"));
 int indexOfRange(Object o, int start, int end) {
 //name为传入参数,o指向name对象,name为上转型对象
        Object[] es = elementData;
        if (o == null) {/name指向的字符串不为null,if语句不执行
            for (int i = start; i < end; i++) {
                if (es[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = start; i < end; i++) {//for遍历该数组获取其下标
                if (o.equals(es[i])) {
                //拿Jimmy对象和各个元素进行比较判断,这是我们会发现两者字符相同
                    return i;//返回其相等后的坐标值给上面的contains方法,最后返回true。
                }
            }
        }
        return -1;
    }
2。判断自定义类型

接下来我们创建Student类型对象,向ArrayList数组添加元素。这里就会发现结果为false,两者值虽然相同,但两者都是创建的新的对象,两者的内存地址不相等,自定义类型在比较时用的是Object的equals方法(比较地址是否相同),所以返回为false。

List<Student> list = new ArrayList<>();
list.add(new Student("Tom"));
String student= new Student("Tom");
System.out.println(list.equals(student)));
    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])) {
                //在这里比较时equals用的是Object的equals方法(比较地址是否相同)
                    return i;
                }
            }
        }
        return -1;//所以两者地址不相同,返回-1值上面contains方法最终返回false。
    }

3。重写equals方法

通过上述自定义类型很难满足我们的需求,当两者内容相同,数据类型相同时我们想让他们相等,这里我们就要对Object里的equals方法进行重写操作。

    @Override
    public boolean equals(Object obj) {
        
        if (obj instanceof Student) {
            Student other = (Student) obj;
            return Objects.equals(this.name, other.name);
        }
        return false;
    }

 

上一篇:C#中Linq的Join比Where(m=>arr..Contains(m.id))的效率更高


下一篇:contains