剖析Java中在Collection集合中使用contains和remove为什么要重写equals

网友投稿 364 2022-12-06

剖析Java中在Collection集合中使用contains和remove为什么要重写equals

目录引言源码剖析实例测试String类和包装类的特殊情况自定义类型总结

引言

在Collection集合中:

contains方法是判断一个集合里面是否包含指定元素,如果有则返回true;

remove方法是从集合中删除指定元素的单个实例;

这两个方法看起很简单,用起来也很简单,同样也非常常用;但是,它们到底是怎么匹配到相应的元素呢?

源码剖析

以ArrayList为例,我们分析一下ArrayList中的contains和remove的源码;

先看看contains:

这里看到比较的对象是一个Object类,变量名为 o(就是是否包含 o ),并且调用了一个indexOf方法,接下来我们进一步看看indexOf源码:

可以看到,indexOf又进一步调用了indexOfRange方法,我们还需要深入看看这个方法:

这里可以发现,indexOfRange中 o 调用了equals方法(蓝色部分)!

我们知道:equals方法是判断两个对象是否相等,但是默认情况下比较的是对象的地址,如果想要比较对象的内容就需要重写equals方法;

那么这个contains调用了equals方法,所以,contains判断一个集合中是否包含某个元素其实就是通过对象地址比较的了;

这并不是我们想要的结果,所以几乎所有放在集合中的类型,都需要重写equals方法!

为什么是几乎所有?

因为还是有特例的:SUN公司已经把String类和包装类的equals方法重写了,所以对于这两种我们不需要重写equals!

同样看看remove方法:

同样,remove方法也是通过equals方法比较元素然后移除的;

所以这里可以得出一个结论:

Collection集合中的remove方法和contains方法底层都会调用equals,所以只要放在集合中的类型,都要重写equals方法;

因为对对象的地址的比较没有什么意义,我们实际上需要的是对象内容间的比较;

实例测试

知道了结论,就来写几个代码测试一下:

String类和包装类的特殊情况

对于 String类型和包装类,SUN公司重写了equals方法,所以我们先测试一下这两种情况:

import java.util.ArrayList;

import java.util.Collection;

// 结论:Collection接口中的remove方法和contains方法底层都会调用equals,

// 所以存放在一个集合中的类型,要重写它的equals方法

// (但是String和包装类的equals方法已经重写过了,不用重写)

public class CollectionTest02 {

public static void main(String[] args) {

// 这里以ArrayList为例

Collection array1 = new ArrayList();

// String类:

String s1 = "Hello";

// 将s1放入array1

array1.add(s1);

// 定义一个s2也为 "Hello",那么调用contains是否会包含s2?

String s2 = "Hello";

System.out.println("array1是否包含s2?" + array1.contains(s2)); // true

// 因为array1中放入的是s1,如果移除s2,s1会不会被移除呢?

array1.remove(s2);

System.out.println("移除s2后s1是否还在array1中?" + array1.contains(s1)); // false

// 包装类也同样:

Integer num1 = 100cJocTB0;

// 将num1放入array1

array1.add(num1);

// 定义一个num2也为1000

Integer num2 = 1000;

System.out.println("array1是否包含num2?" + array1.contains(num2)); // true

// 移除num2观察num1是否会被移除

array1.remove(num2);

System.out.println("移除num2后num1是否还在array1中?" + array1.contains(num1)); // false

}

}

输出结果:

array1是否包含s2?true

移除s2后s1是否还在array1中?false

array1是否包含num2?true

移除num2后num1是否还在array1中?false

自定义类型

这是equals重写的情况,接下来我自定义一个类型,看看没有重写会发生什么;

import java.util.ArrayList;

import java.util.Collection;

public class CollectionTest03 {

public static void main(String[] args) {

// 还是以ArrayList为例

Collection array = new ArrayList();

// 创建一个User对象u1

User u1 = new User("张三");

// 将u1对象放入array中

array.add(u1);

// 再创建一个User对象,使其内容和u1相同都为"张三",那么调用contains是否会包含该对象呢?

System.out.println("array中是否包含新创建的对象?" + array.contains(new User("张三"))); // false

// 移除一个内容为"张三"的新的对象,u1是否会被移除?

array.remove(new User("张三"));

System.out.println("移除后u1是否存在?" + array.contains(u1)); // true

}

}

// 自己定义一个User类

class User {

// 成员变量:姓名

private String name;

// 默认构造

User() {}

// 有参构造:初始化姓名

User(String name) {

this.name = name;

}

}

输出结果:

array中是否包含新创建的对象?false

移除后u1是否存在?true

可以看到,我自定义的User方法里没有重写equals方法,所以当调用contains和remove时,虽然传入的对象内容和u1的对象内容相同都为“张三”,但是实际上比较的却是对象的地址;

接下来我重写User的equals方法,看看结果如何;

import java.util.ArrayList;

import java.util.Collection;

public class CollectionTest03 {

public static void main(String[] args) {

// 还是以ArrayList为例

Collection array = new ArrayList();

// 创建一个User对象u1

User u1 = new User("张三");

// 将u1对象放入array中

array.add(u1);

// 再创建一个User对象,使其内容和u1相同都为"张三",那么调用contains是否会包含该对象呢?

System.out.println("array中是否包含新创建的对象?" + array.contains(new User("张三"))); // true

// 移除一个内容为"张三"的新的对象,u1是否会被移除?

array.remove(new User("张三"));

System.out.println("移除后u1是否存在?" + array.contains(u1)); // false

}

}

// 自己定义一个User类

class User {

// 成员变量:姓名

private String name;

// 默认构造

User() {}

// 有参构造:初始化姓名

User(String name) {

this.name = name;

}

// 重写equals方法 ,通过name进行比较

public boolean equals(Object o) {

if (this == o) return true;

if (!(o instanceof User)) return false;

User user = (User) o;

return name.equals(user.name);

}

}

输出结果:

array中是否包含新创建的对象?true

移除后u1是否存在?false

我只是重写了一个equals方法,其他地方都没变,结果完全不同;

所以这就验证了之前的结论:contains 和 remove 底层实现都调用了equals方法;

总结

其实这篇文章就是分析一下contains 和 remove 底层实现,主要想说的还是那一句话:

Collection集合中的remove方法和contains方法底层都会调用equals,所以只要放在集合中的类型,都要重写equals方法;(String和包装类除外)

希望各位在Java学习中养成好习惯,要时刻惦记着equals的重写,不然如果做了一个项目你连错在哪里都不好找到;

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java中你真的会用Constructor构造器吗之看完本篇你就真的会了
下一篇:Java8 Lambda和Invokedynamic详情
相关文章

 发表评论

暂时没有评论,来抢沙发吧~