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

 更新时间:2021年09月22日 09:14:10   作者:YXXYX  
这篇文章主要介绍了Collection集合的contains和remove方法详解remove以及相关的经验技巧,通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

引言

在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 = 1000;
        // 将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的重写,不然如果做了一个项目你连错在哪里都不好找到;

到此这篇关于剖析在Collection集合中使用contains和remove为什么要重写equals的文章就介绍到这了,更多相关Collection 重写 equals内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 学生信息管理系统java版

    学生信息管理系统java版

    这篇文章主要为大家详细介绍了java学生信息管理系统源代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • Mybatis Web中的数据库操作方法举例详解

    Mybatis Web中的数据库操作方法举例详解

    Mybatis是一款优秀的持久化框架,用于简化JDBC的开发,下面这篇文章主要给大家介绍了关于Mybatis Web中数据库操作方法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-09-09
  • Java集合框架之LinkedHashSet类解读

    Java集合框架之LinkedHashSet类解读

    这篇文章主要介绍了Java集合框架之LinkedHashSet类解读,LinkedHashSet是HashSet的有序版本,它跨所有元素维护一个双向链接的List,当需要维护迭代顺序时,就使用这个类,当遍历HashSet时,顺序是不可预测的,需要的朋友可以参考下
    2023-09-09
  • Java基础知识精通数组的使用

    Java基础知识精通数组的使用

    数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java 语言中提供的数组是用来存储固定大小的同类型元素
    2022-04-04
  • 详解Java中ThreadLocal类型及简单用法

    详解Java中ThreadLocal类型及简单用法

    ThreadLocal实例通常是希望将状态与线程关联起来的类中的私有静态字段,下面通过例子给大家详细介绍Java中ThreadLocal类型及简单用法,感兴趣的朋友跟随小编一起看看吧
    2021-10-10
  • MybatisPlus之时间处理问题

    MybatisPlus之时间处理问题

    在数据库设计时,阿里巴巴编码规约建议使用gmt_create和gmt_modified命名时间字段,并设置为datetime类型,本文介绍了两种自动填充时间字段的实现方式:SQL级别和代码级别(使用MyBatis Plus),SQL级别通过设置默认值和更新值为CURRENT_TIMESTAMP
    2024-09-09
  • 详解Spring 框架中切入点 pointcut 表达式的常用写法

    详解Spring 框架中切入点 pointcut 表达式的常用写法

    这篇文章主要介绍了详解Spring 框架中切入点 pointcut 表达式的常用写法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • 通过代理类实现java连接数据库(使用dao层操作数据)实例分享

    通过代理类实现java连接数据库(使用dao层操作数据)实例分享

    java通过代理类实现数据库DAO操作代码分享,大家参考使用吧
    2013-12-12
  • Spring MVC中自定义拦截器的实例讲解

    Spring MVC中自定义拦截器的实例讲解

    下面小编就为大家带来一篇Spring MVC中自定义拦截器的实例讲解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Springboot2.x+ShardingSphere实现分库分表的示例代码

    Springboot2.x+ShardingSphere实现分库分表的示例代码

    这篇文章主要介绍了Springboot2.x+ShardingSphere实现分库分表的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10

最新评论