Java避免踩坑:Set对象排重注意避免重复-以commons-csv读取csv文件并排查为例

news2024/11/25 13:34:08

场景

HashSet

HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。HashSet 允许有 null 值。

HashSet 是无序的,即不会记录插入的顺序。 HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,

则最终结果是不确定的。

在Java语言中,Set数据结构可以用于对象排重,常见的Set类有HashSet、LinkedHashSet等。

比如:

代码中使用HashSet数据结构,为了避免城市数据重复,对读取的城市数据进行强制排重。

这里的数据源从csv文件中读取。

 

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主

读取csv文件内容的方式有很多种,这里使用apache的commons-csv的方式。

首先项目中引入依赖

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-csv</artifactId>
            <version>1.7</version>
        </dependency>

然后新建读取文件和解析数据的工具类,这里是读取城市数据,所以是

CityHelper

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;

import java.io.*;
import java.util.*;

public class CityHelper {
    public static Collection<City> readCities(String fileName){
        try (
            FileInputStream stream = new FileInputStream(fileName);
            InputStreamReader reader = new InputStreamReader(stream,"GBK");
            CSVParser parser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())
        )
        {
            Set<City> citySet = new HashSet<>(1024);
            Iterator<CSVRecord> iterator = parser.iterator();
            while (iterator.hasNext()){
                citySet.add(parseCity(iterator.next()));
            }
            return citySet;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Collections.emptySet();
    }
   
    /**
     * 解析城市
     * @param record
     * @return
     */
    private static City parseCity(CSVRecord record){
        City city = new City();
        city.setCode(record.get(0));
        city.setName(record.get(1));
        return city;
    }
}

然后新建City实体类

public class City{
    private String code;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

下面进行业务测试

        Collection<City> cities = CityHelper.readCities("D:\\test.csv");
        System.out.println(cities);

查看输出结果

 

竟然没有实现去重效果。

这里注意踩坑:

原因分析:

当向集合Set中增加对象时,首先集合计算要增加对象的hashCode,根据该值来得到一个位置用来存放当前对象。

如在该位置没有一个对象存在的话,那么集合Set认为该对象在集合中不存在,直接增加进去。

如果在该位置有一个对象存在的话,接着将准备增加到集合中的对象与该位置上的对象进行equals方法比较:

如果该equals方法返回false,那么集合认为集合中不存在该对象,就把该对象放在这个对象之后;

如果equals方法返回true,那么就认为集合中已经存在该对象了,就不会再将该对象增加到集合中了。

所以,在哈希表中判断两个元素是否重复要使用到hashCode方法和equals方法。

hashCode方法决定数据在表中的存储位置,而equals方法判断表中是否存在相同的数据。

分析上面的问题,由于没有重写City类的hashCode方法和equals方法,就会采用Object类的hashCode方法和equals方法。

Object类的hashCode方法是一个本地方法,返回的是对象地址;Object类的equals方法只比较对象是否相等。

所以,对于两条完全一样的北京数据,由于在解析时初始化了不同的City对象,导致hashCode方法和equals方法值都不一样,

必然被Set认为是不同的对象,所以没有进行排重。

解决Java中使用HashSet进行排重时不生效问题

解决:重写City类的hashCode方法和equals方法

这里我们再新建一个City2实体类并修改如下

import java.util.Objects;

/**
 * 城市类
 */
public class City2 {

    private String code;
    private String name;

    /**
     * 判断相等
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj) {
        if(obj == this){
            return true;
        }
        if(Objects.isNull(obj)){
            return false;
        }
        if(obj.getClass() != this.getClass()){
            return false;
        }
        return Objects.equals(this.code,((City2)obj).getCode());
    }

    /**
     * 哈希编码
     * @return
     */
    @Override
    public int hashCode() {
        return Objects.hashCode(this.code);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

这里重写的equals方法中指定了自己需要的逻辑,根据code字段判断,如果相等则认为相同。

然后再重新调用并查看结果

 

总结:

1、当确定解析的城市数据唯一时,就没有必要进行排重操作,可以直接使用List来存储。

2、确定解析的城市数据不唯一时,需要按照城市名称进行排重操作,可以直接使用Map进行存储。

为什么不建议实现City类的hashCode方法,再采用HashSet来实现排重呢?

 首先,不希望把业务逻辑放在模型DO类中;其次,把排重字段放在代码中,便于代码的阅读、理解和维护。

3、不重写hashCode方法和equals方法的自定义类不应该在Set中使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/627810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Classification-based framework for binarization on OCT-ME论文学习和总结

论文&#xff1a;Classification-based framework for binarization on mice eye image in vivo with optical coherence tomography 源码&#xff1a;https://github.com/MIP2019/mip2019.github.io/blob/main/spsvm 目录 一、背景和出发点 二、创新点 三、SPSVM的具体实现…

hive创建udf函数流程

1.编写udf函数 引入pom文件 <dependencies> <dependency> <!-- 这个属于额外的jar包 自己按需引用 比如你想搞得函数 里面要连接mysql 这里肯定需要引入mysql的驱动包 我这个包是为了计算字符串的表达式的。 --> <groupId>org.apache.com…

Git GitLab 使用及规范

Git 基本操作 Git安装配置及基本使用 从官网下载安装包&#xff0c;手动完成安装。打开Git Bash命令行工具&#xff0c;执行命令ssh-keygen -t rsa -C Email-Addresss生成一个密钥对。登录到GitLab&#xff0c;点击右上角你的用户头像&#xff0c;点击Edit Profile settings&…

【Linux】linux和Linus

1991.09.17 21岁的芬兰学生林纳斯.托瓦兹在网上发布开源操作系统Linux0.01。 林纳斯本纳第克特托瓦兹&#xff08;Linus Benedict Torvalds&#xff0c;1969年12月28日- &#xff09;&#xff0c;芬兰赫尔辛基人&#xff0c;著名的电脑程序员&#xff0c;Linux内核的发明人及 …

Revit如何在体量中进行放样及如何生成垫层

一、Revit如何在体量中进行放样 体量中的放样在常规族放样的基础上进行了简化&#xff0c;下面通过实例来说明如何在体量中进行放样。 &#xff08;1&#xff09;新建概念体量。点击“新建概念体量”→“公制体量”→“打开”。 &#xff08;2&#xff09;绘制放样路径。点击“…

盖雅「劳动力账户」助力物业行业实现精细化工时成本管理

物业行业的用工形式和工时制度多样&#xff0c;需要大量的劳动力提供安保、清洁、维修及其他服务&#xff0c;所以人工成本成为了物业公司最大的经营成本之一。而这些员工近半数都是外包人员。因此&#xff0c;物业公司需要利用数字化工具实时记录员工的出勤和工时&#xff0c;…

Grid++Report多个子报表实现

子报表实现参考 GridReport子报表实现 基于上述单个子报表的实现&#xff0c;我们可以衍生多个子报表实现 子报表与主报表可以是关联关系&#xff0c;也可以是独立存在。 配置主从关系字段即代表有关联关系 只有明细网格节点内配置的子报表才可以设置主从关系字段 报表头、…

强光LED手电筒方案开发设计

在户外活动中&#xff0c;不管是徒步还是露营&#xff0c;经常需要使用多功能强光手电筒。宇凡微推出的多功能战术强光LED手电筒方案&#xff0c;具有十多年LED灯项目研发经验&#xff0c;方案成熟&#xff0c;支持定制开发。 一、战术强光LED手电筒方案功能介绍 户外使用的LED…

安装Logstash并导入Movielens测试数据集(基于elasticsearch-8.5.2版本)

安装Logstash并导入Movielens测试数据集 0 安装前准备工作 0.1 安装包下载 组件安装包下载地址Logstashlogstash-8.5.2-linux-x86_64.tar.gzelastic官网&#xff1a;https://www.elastic.co/cn/downloads/past-releases#logstash elastic中文社区&#xff1a;https://elasti…

第六届中国软件开源创新大赛-openGauss赛道全面开启

第六届“中国软件开源创新大赛”在国家自然科学基金委信息科学部的指导下&#xff0c;由中国计算机学会&#xff08;CCF&#xff09;主办&#xff0c;西北工业大学、绿色计算产业联盟、CCF 开源发展委员会联合承办。旨在为国内开源社区提供展示、交流、合作的平台&#xff0c;激…

实力出圈!联诚发LED屏与xr虚拟拍摄解决方案亮相文博会!

六月初夏的深圳&#xff0c;正迎来一场文化产业界的盛事。6月7日&#xff0c;为期5天的第十九届中国(深圳)国际文化产业博览交易会(下称“文博会”)正式拉开帷幕。联诚发LCF作为国家级高新技术企业、国家级专精特新小巨人企业&#xff0c;以及优秀的LED显示与数字装备企业代表亮…

03.Web大前端时代之:HTML5+CSS3入门系列~H5功能元素

2.功能元素 1.hgroup 对网页或区段&#xff08;section&#xff09;的标题进行组合 2.figure <figure> 标签规定独立的流内容&#xff08;图像、图表、照片、代码等等&#xff09;。 figure 元素的内容应该与主内容相关&#xff0c;但如果被删除&#xff0c;则不应对…

如何申请项目管理专业人员能力等级评价(CSPM)?

2021年10月&#xff0c;中共中央、国务院发布的《国家标准化发展纲要》明确提出构建多层次从业人员培养培训体系&#xff0c;开展专业人才培养培训和国家质量基础设施综合教育。建立健全人才的职业能力评价和激励机制。由中国标准化协会&#xff08;CAS&#xff09;组织开展的项…

“智慧水利”发展综述:我国水利事业迈入新阶段

6月7日-9日&#xff0c;“2023中国水博览会暨中国&#xff08;国际&#xff09;水务高峰论坛”上&#xff0c;“智慧水利”再次成为热议话题。 智慧水利是在以智慧城市为代表的智慧型社会建设中产生的相关先进理念和高新技术在水利行业的创新应用&#xff0c;是云计算、大数据、…

泰山信息科技5周年:无尽的感恩,非常非常的惋惜

去年的时候&#xff0c;庆贺4周年&#xff0c;公司员工一起去某个地方玩&#xff08;确实没吃到什么东西&#xff09;。这是当时的情形&#xff1a; 因为各种原因&#xff0c;今年3月无锡研发基地解散。作为技术总监&#xff0c;我是非常非常的惋惜。因为我真的想把泰山OFFICE做…

T2-U开发板实现红外遥控接收与发送

文章目录 一、红外概况二、发射1. 调制2. 红外传输协议3. 编码 三、接收四、T2-U开发板硬件连接五、TuyaOS红外功能介绍红外接收功能 六、红外功能代码使用流程1. 硬件注册2. 设备查找3. 打开设备4. 红外发送5. 红外接收6. 接收回调注册 红外遥控是利用近红外光进行数据传输的一…

如何做好功能测试,看这几点就够了

关于新人如何做好功能测试&#xff0c;以下是我个人的一些思考。 01、测试基础的重要性 作为一名测试新人&#xff0c;测试基础非常非常重要。这里说的基础&#xff0c;不仅仅是什么是软件测试、软件测试的目的&#xff0c;而是测试用例的设计能力。 因工作的原因&#xff0…

进程信号以及用户态和内核态的理解

文章目录 什么是进程信号&#xff1f;用户层产生信号的方式有哪些&#xff1f;信号在内核的存在形式认识信号的一些接口信号处理的执行流程理解用户态和内核态信号处理流程 什么是进程信号&#xff1f; 进程信号是一种事件异步通知机制&#xff0c;属于软件中断&#xff08;因…

手机充电流程

手机的整个充电过程由充电管理IC 控制的&#xff0c;完整的充电过程可以分为6个阶段&#xff1a;涓流充电模式、预充电模式、恒流充电模式、恒压充电模式、充电完成模式、二次充电模式。 完整的充电过程 1. 涓流充电模式 涓流充电一般以很小的电流&#xff08;几十到一百毫安…

LeetCode - 15 三数之和

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满…