Administrator
发布于 2022-03-06 / 4 阅读
0
0

深究contains方法

contains方法解析

在java中,contains方法用于判断集合中是否包含某个元素,包含则返回true,否则返回false。

boolean contains(Object o); //

先看一个案例:字符串str实际上没添加进集合c里,但是contains返回的结果却是true?也就是说,contains发现str包含在c集合里。

public static void main(String[] args) {
    //创建集合对象
        Collection c = new ArrayList();
    //添加数据
        c.add("abc");
        c.add(123);

        //1.str实际上没添加进集合c里,但是contains返回的结果却是true?
        String str = "abc";
        System.out.println("str是否包含在集合里:"+c.contains(str)); //true
     

1.png

再看第二个案例:new了两个属性相同的学生对象stu1和stu2。跟代码一逻辑一样,把stu1添加进集合里,因为两个学生的名字是相同的,所以用contains方法比较stu2是否包含在集合c里,然而这次返回结果是false,为什么,明明两个学生名字一样啊。

public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();
    
        //添加数据
        c.add("abc");
        c.add(123);

        //2.根据上面的逻辑,这次new两个属性相同的学生对象stu1和stu2,把stu1添加进集合里,然后判断stu2是否在集合里,这次结果为false
        Student stu1 = new Student("小明");
        Student stu2 = new Student("小明");
    
        c.add(stu1);
    
        System.out.println("学生对象stu是否在集合c里:"+c.contains(stu2));    //false
    }
}
//学生类
class Student{
    String name;

    //带参构造方法
    public Student(String name) {
        this.name = name;
    }

2.png

原因:

​ 通过查看实现类ArrayList里的contains方法可以发现,从contains()到indexOf(),再到indexOfRange(),在最后的indexOfRange()方法里,调用了equals()方法来进行比较。

//contains的底层源码
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;
}

结论分析

​ 关于equals方法,我们得知道,所有的子类默认继承Object类,而Object的equels方法只是返回对象内存地址。

​ 由此分析代码一:集合里有一个字符串“abc”,创建一个String变量赋值“abc”,两者本质上都是字符串,且String类型已经重写equals方法。contains在对两个比较的时候调用的是equals方法,由于String类的已经重写了,返回的是属性而不是内存地址,故字符串可以被正确比较。

​ 代码二分析:虽然两个学生的名字属性一样,但是,集合保存的是对象的内存地址,而Student类没有重写equals方法,所以contains方法在调用equals方法判断的时候,判断的是两个对象的地址,两个对象的内存地址肯定不一样,所以返回false。

​ 代码二解决方案:重写Student的equals方法,使其返回对象属性,即可获取正确的判断。
3.png

//学生类重写的equals方法
class Student{
    String name;

    public Student(String name) {
        this.name = name;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof Student)) return false;
        if(obj == this) return true;
        Student s = (Student) obj;
        if(this.name == s.name) return true;
        return false;
    }
}

再看以下代码:

Integer x = 1000;
Integer y = 1000;
c.add(x);
System.out.println(c.contains(y));    //true

虽然new了两个不一样的对象,但是Integer包装类重写了equals方法,所以x和y是一样的。

结论:放在集合里的类型,一定要重写equals方法。

注意:remove删除方法也是调用的equals方法。


评论