剖析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 = 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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中Collections.emptyList()的注意事项

    偶然发现有小伙伴错误地使用了Collections.emptyList()方法,这里记录一下.她的使用方式是: public void run() { ...... List list = buildList(param); ...... Object newNode = getNode(...); list.add(newNode); ...... } public List buildList(Object param) { if (isInValid(param)) { return Co

  • 在Java中Collection的一些常用方法总结

    Java中Collection的常用方法 1.add() 向中添加元素 add(100) 自动装箱操作,实际上是放进去的一个对象, Integer n = new Integer(100),实际上是把n放进了 Collection co = new ArrayList(); co.add(1); 2.addAll( Collection c ) 将指定集合中的所有元素添加到从集合中 因为ArryList类中重写了equals() 方法,所以两个集合比较相等. public class lxc {

  • java Collections 排序--多条件排序实例

    我就废话不多说了,大家还是直接看代码吧~ // 告警排序 Collections.sort(domesticAirport, comparator); // 告警排序 Comparator<AirportRtWeatherWarningBeanForTable> comparator = new Comparator<AirportRtWeatherWarningBeanForTable>() { @Override public int compare(AirportRtWeat

  • JAVA容器集合全面解析(Collection和Map)

    目录 前言 一.Collection集合 1.1List集合 1.1.1ArrayList集合 1.1.2LinkedList集合 1.2Set集合 1.2.1HashSet集合 HashSet集合保证元素唯一性源码分析: 1.2.2TreeSet集合 比较器排序Comparator的使用: 二.Map集合 2.1Map集合的概述与特点 2.2Map集合的获取功能 2.3Map集合的遍历方式(方式一) 2.4Map集合的遍历方式(方式二) 2.5HashMap集合 前言 本次我将分享的是java

  • 深入浅出讲解Java集合之Collection接口

    目录 一.集合框架的概述 二.集合框架(Java集合可分为Collection 和 Map 两种体系) 三.Collection接口中的方法的使用 四.集合元素的遍历操作 A. 使用(迭代器)Iterator接口 B. jdk5.0新增foreach循环,用于遍历集合.数组 五.Collection子接口之一:List接口 List接口方法 ArrayList的源码分析: JDK 7情况下: JDK 8中ArrayList的变化: LinkedList的源码分析: Vector的源码分析: 六.

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

    目录 引言 源码剖析 实例测试 String类和包装类的特殊情况 自定义类型 总结 引言 在Collection集合中: contains方法是判断一个集合里面是否包含指定元素,如果有则返回true: remove方法是从集合中删除指定元素的单个实例: 这两个方法看起很简单,用起来也很简单,同样也非常常用:但是,它们到底是怎么匹配到相应的元素呢? 源码剖析 以ArrayList为例,我们分析一下ArrayList中的contains和remove的源码: 先看看contains: 这里看到比较的

  • Java提取2个集合中的相同和不同元素代码示例

    本文分享的示例代码实现提取2个集合中相同和不同的元素 此处需要使用Collection集合所提供的一个方法:removeAll(Cellection list),removeAll方法用于从列表中移除指定collection中包含的所有元素. 语法 removeAll(Collection<?> c) c:包含从列表中移除元素的collection对象. 该方法返回值为boolean对象,如果List集合对象由于调用removeAll方法而发生更改,则返回true,否则返回false.实现代码

  • 在java中获取List集合中最大的日期时间操作

    取List集合中最大的日期, 可以用Date max = Collections.max(dateList);, 传入一个日期集合, 就可以获取, 工作中有这个需求, 就查找到这个, 代码如下 } else { /** 获取此专题下的所有内容的最新时间 */ Long featureId = this.communityFeatureMapper.selectFeatureIdByLabelId(labelId); List<CommunityFeatureRelation> communit

  • Java 详解Collection集合之ArrayList和HashSet

    目录 Collection List ArrayList Set HashSet ArrayList和HashSet的区别 泛型 Collection Collection接口被List接口和Set接口继承 本章只介绍常用的集合 List ArrayList是List接口的实现类 ArrayList ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素. ArrayList 继承了 AbstractList ,并实现了 List 接口

  • Java 详解Collection集合之ArrayList和HashSet

    目录 Collection List ArrayList Set HashSet ArrayList和HashSet的区别 泛型 Collection Collection接口被List接口和Set接口继承 本章只介绍常用的集合 List ArrayList是List接口的实现类 ArrayList ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素. ArrayList 继承了 AbstractList ,并实现了 List 接口

  • Java中的collection集合类型总结

    Java集合是java提供的工具包,包含了常用的数据结构:集合.链表.队列.栈.数组.映射等.Java集合工具包位置是java.util.* Java集合主要可以划分为4个部分:List列表.Set集合.Map映射.工具类(Iterator迭代器.Enumeration枚举类.Arrays和Collections). Java集合工具包框架如下图. 说明:看上面的框架图,先抓住它的主干,即Collection和Map. Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作

  • Java中Collection集合的常用方法详解

    目录 1.boolean add(E e) 2.boolean addAll(Collection<? extends E> c) 3.void clear() 4.boolean contains(Object o) 5.boolean containsAll(Collection<?> c) 6.boolean equals(Object o) 7.int hashCode() 8.boolean isEmpty() 9.Iterator<E> iterator()

  • 详解Java中Collection集合的常用方法

    目录 1.boolean add(E e) 2.boolean addAll(Collection<? extends E> c) 3.void clear() 4.boolean contains(Object o) 5.boolean containsAll(Collection<?> c) 6.boolean equals(Object o) 7.int hashCode() 8.boolean isEmpty() 9.Iterator<E> iterator()

  • java 如何实现正确的删除集合中的元素

    在java中如果我们需要遍历集合并删除其中的某些元素时,例如对于List来说,我们有三种办法. 1. 普通的for循环遍历并删除 public void forRemove(List<T> list, T obj){ for(int i = 0;i < list.size(); i++){ if (obj == list.get(i)) { list.remove(obj); } } } main中调用 <pre name="code" class="

  • Java中的set集合是什么意思

    目录 引言 概念 HashSet集合 LinkedHashSet集合: TreeSet集合: 实战场景 引言 在前面的内容中,我们先是一一介绍了Collection集合中都有哪些种类的集合,并且详细地讲解了List集合中的相关知识,那么今天我们来详细地讲解一下Collection集合中的另外一个分支——Set系列集合.最后还是希望这一篇篇的文章能够对你在集合中的学习.Java中的学习起到一定的帮助作用,好了,闲话不多说直接步入正题吧. 概念 Set系类集合特点: 无序:存取顺序不一致 不重复:可

随机推荐