Java中对List对象集合使用HashSet自动去重无法生效问题的解决

一、需求描述

  1. 使用SQL语句关联三个表后,在Debug测试的过程中,获取数据库中的记录,发现有好几条记录是重复的。经过问题的排查,导致关联的结果出现重复,是因为其中一个表中含有多条重复的历史记录。
  2. 需要有一种办法将返回的数据进行条件过滤,将重复的记录只保持一条,所以想到了Set集合。

二、补充

  1. Set集合的特点(其中2点):
    (1)集合元素不重复
    (2)集合元素无序
  2. 底层原理简述
    HashSet底层是HashMap实现,value固定为一个自定义对象,使用key保证集合元素的唯一性(原理:确保元素唯一性的两个方法,hashCode()和equals()方法。当调用add()方法向集合存入对象时,先比较此对象与原有对象的哈希值是否一样,如果不一样,直接存入集合,如果哈希值一样,就会继续比较这两个对象是否为同一个对象,就会调用equals()方法)总之,只有HashCode的相同时,才会调用equals()方法。但它不保证集合元素的顺序(HashSet底层HashMap一定无序)

三、去重过程

//主函数如下所示:
 public static void main(String[] args) {
        List<Staff> list = new ArrayList<>();
        list.add(new Staff(1,"李华","lihua@163.com",111,"13115448748","隔壁小王","人工智能系统"));
        list.add(new Staff(1,"李华","lihua@163.com",111,"13115448748","隔壁小王","人工智能系统"));
        Set<Staff> set = new HashSet<>(list);
        //set.addAll(list);
        List<Staff> list1 = new ArrayList<>(set);
        System.out.println(list1.size());
    }
//最初的员工实体类
package com.ml.entry;
public class Staff {
    private Integer managerId;
    private String manager;
    private String manager_email;
    private Integer staffId;
    private String staffNum;
    private String staffName;
    private String sysName;

    public Integer getManagerId() {
        return managerId;
    }

    public void setManagerId(Integer managerId) {
        this.managerId = managerId;
    }

    public String getManager() {
        return manager;
    }

    public void setManager(String manager) {
        this.manager = manager;
    }

    public String getManager_email() {
        return manager_email;
    }

    public void setManager_email(String manager_email) {
        this.manager_email = manager_email;
    }

    public Integer getStaffId() {
        return staffId;
    }

    public void setStaffId(Integer staffId) {
        this.staffId = staffId;
    }

    public String getStaffNum() {
        return staffNum;
    }

    public void setStaffNum(String staffNum) {
        this.staffNum = staffNum;
    }

    public String getStaffName() {
        return staffName;
    }

    public void setStaffName(String staffName) {
        this.staffName = staffName;
    }

    public String getSysName() {
        return sysName;
    }

    public void setSysName(String sysName) {
        this.sysName = sysName;
    }

    @Override
    public String toString() {
        return "Staff{" +
                "managerId=" + managerId +
                ", manager='" + manager + '\'' +
                ", manager_email='" + manager_email + '\'' +
                ", staffId=" + staffId +
                ", staffNum='" + staffNum + '\'' +
                ", staffName='" + staffName + '\'' +
                ", sysName='" + sysName + '\'' +
                '}';
    }
}

//修改之后的员工实体类
package com.ml.entry;
public class Staff {
    private Integer managerId;
    private String manager;
    private String manager_email;
    private Integer staffId;
    private String staffNum;
    private String staffName;
    private String sysName;

   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Staff staff = (Staff) o;
        return Objects.equals(managerId, staff.managerId) &&
                Objects.equals(manager, staff.manager) &&
                Objects.equals(manager_email, staff.manager_email) &&
                Objects.equals(staffId, staff.staffId) &&
                Objects.equals(staffNum, staff.staffNum) &&
                Objects.equals(staffName, staff.staffName) &&
                Objects.equals(sysName, staff.sysName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(managerId, manager, manager_email, staffId, staffNum, staffName, sysName);
    }
    public Integer getManagerId() {
        return managerId;
    }

    public void setManagerId(Integer managerId) {
        this.managerId = managerId;
    }

    public String getManager() {
        return manager;
    }

    public void setManager(String manager) {
        this.manager = manager;
    }

    public String getManager_email() {
        return manager_email;
    }

    public void setManager_email(String manager_email) {
        this.manager_email = manager_email;
    }

    public Integer getStaffId() {
        return staffId;
    }

    public void setStaffId(Integer staffId) {
        this.staffId = staffId;
    }

    public String getStaffNum() {
        return staffNum;
    }

    public void setStaffNum(String staffNum) {
        this.staffNum = staffNum;
    }

    public String getStaffName() {
        return staffName;
    }

    public void setStaffName(String staffName) {
        this.staffName = staffName;
    }

    public String getSysName() {
        return sysName;
    }

    public void setSysName(String sysName) {
        this.sysName = sysName;
    }

    @Override
    public String toString() {
        return "Staff{" +
                "managerId=" + managerId +
                ", manager='" + manager + '\'' +
                ", manager_email='" + manager_email + '\'' +
                ", staffId=" + staffId +
                ", staffNum='" + staffNum + '\'' +
                ", staffName='" + staffName + '\'' +
                ", sysName='" + sysName + '\'' +
                '}';
    }
}

运行主函数测试结果显示,通过Set集合自动去重之后,集合中还是含有两个对象,并没有去重。
通过仔细的分析,问题原来出现在员工实体类对象中。原来对于自定义的实体类,若想通过Set集合对List集合达到去重效果的话,需要在自定义的实体类中重写hashCode()和equals()方法。

四、过程分析

  1. 首先我们看一下HashSet的具体实现,就拿add()方法来分析一下。
    在这里插入图片描述
    方框1处分析:

  2. HashSet集合对象是如何判断数据元素是否重复的呢?
    (1)首先会检查待存对象key的hashCode值是否与集合中已有元素对象hashCode值相同,如果hashCode不同则表示不重复, 如果hashCode相同那么会再调用equals方法进一步检查,equals返回true表示对象已存在,否则表示不存在。
    注意: 在Java中,hashCode值是由存储对象的地址所确定的,每一个地址对应一个hashCode值。

  3. 所以回到我们问题初始的地方,似乎就能找到答案了,当我们在向Staff对象添加两个相同对象值的时候,为什么经过HashSet处理之后,理应去重,但是实际输出结果却未去重。

  4. 原因就在于我们通过使用new Staff();对象创建实际是创建了两个不同的对象,也就对应了两个地址进行存储对象,所以Ha shSet在if判断的时候,对于HashCode不同,就返回了false,证明集合中是两个不同的对象值,这样就使得最后集合中并没有去重。
    在这里插入图片描述
    在这里插入图片描述

  5. 所以通过这个小的案例,我们可以明白一个道理。当我们想要对集合对象达到去重效果的话,我们需要在自定义的Staff类中重写hashCode方法,让id值相同的Staff对象的hashCode值相同,这样才能让id相同的对象通过上面判断条件的第一关。

  6. 两个对象的Student对象就会在4.1方框1的if判断的p.hash == hash返回true而进入下一个判断条件,而由于(k = p.key) == key比较的是两个对象的地址,所以自然是false,接着就进入key != null && key.equals(k)的判断,而由于Staff类中没有equals方法,所以这里调用的是原生的Object类中equals方法,而通过查找我们发现,Object类中的equals方法比对的仍然是对象的地址,这样返回的结果还是false,那么这样肯定是不行的,所以我们这里就需要对Staff类做一些处理;

  7. 解决办法就是在Staff类中重写equals方法,来达到将这两个Student对象判断为一个对象的目的,下图就是我在Staff类中重写的equals方法:
    在这里插入图片描述注意:在重写equals方法的时候,需要考虑到调用这个方法的并不一定是Staff类所创建的对象,这时就需要添加一个if语句去判断调用这个方法的对象是否为Staff类的实例,如果是,那么我们就将它的元素值与集合中已有对象的元素值做对比。

五、过程总结

  1. HashSet达到去重的二要素:
    (1)重写HashCode方法
    (2)重写equals方法
  2. 两个方法重写的目的:
    (1)重写HashCode方法目的是保证两个对象之间的HashCode相同
    (2)重写equals方法目的是保证两个对象为一个相同对象
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值