0

0

Java中根据首字段删除文本数据重复行的策略与实现

霞舞

霞舞

发布时间:2025-12-03 15:48:36

|

811人浏览过

|

来源于php中文网

原创

Java中根据首字段删除文本数据重复行的策略与实现

本教程详细阐述了在java中如何高效地从文本数据中删除基于首字段重复的整行记录。文章提供了两种核心策略:一是利用java stream api的`collectors.tomap`直接处理字符串列表,通过自定义键映射和合并函数实现去重;二是引入领域对象(如`company`类)来封装数据,提高代码可读性和可维护性,并结合stream api进行去重。教程包含示例代码、注意事项及最佳实践,旨在帮助开发者选择合适的去重方案。

在处理文本数据时,经常会遇到需要根据记录中的特定字段来识别并删除重复行的情况。例如,在一个包含公司信息的文本文件中,我们可能希望根据公司的唯一标识符(如ID)来确保每家公司只保留一条记录,即使其他字段有所不同。本教程将探讨两种在Java中实现这一目标的有效方法。

1. 使用Java Stream API直接处理字符串列表

当数据以字符串列表的形式存在时,我们可以利用Java 8引入的Stream API及其强大的Collectors.toMap()方法来实现基于首字段的去重。这种方法简洁高效,适用于快速处理。

核心原理

Collectors.toMap()允许我们指定如何从流中的元素生成键(keyMapper)和值(valueMapper)。更重要的是,它提供了一个mergeFunction来处理当多个元素生成相同的键时如何合并这些值。通过将行的第一个字段作为键,整行作为值,并指定在键冲突时保留第一个遇到的值,即可实现去重。

示例代码

假设我们有一个包含公司信息的字符串列表,每行以逗号分隔,第一个字段是公司ID。

立即学习Java免费学习笔记(深入)”;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class DuplicateRowRemover {

    public static void main(String[] args) {
        List<String> sourceList = List.of(
            "123456,greenwitch street,near dominos store,Opp sandwitch company,Neyork,US,876890",
            "123480,Postwitch street,near KFC store,Opp masala company,Newyork,US,876891",
            "123456,Newyork street,near 100th avenue,King master company,Texas,US,10005"
        );

        List<String> uniqueList = sourceList.stream()
            .collect(Collectors.toMap(
                str -> str.substring(0, str.indexOf(',')), // keyMapper: 提取第一个逗号前的子字符串作为键
                Function.identity(),                       // valueMapper: 整行字符串作为值
                (existing, replacement) -> existing        // mergeFunction: 当键重复时,保留已存在的(即第一个遇到的)值
            ))
            .values()                                      // 获取Map中所有的值(即去重后的行)
            .stream()
            .toList();                                     // 转换为List

        System.out.println("去重后的字符串列表:");
        uniqueList.forEach(System.out::println);
        // 预期输出:
        // 123456,greenwitch street,near dominos store,Opp sandwitch company,Neyork,US,876890
        // 123480,Postwitch street,near KFC store,Opp masala company,Newyork,US,876891
    }
}

在上述代码中:

艺映AI
艺映AI

艺映AI - 免费AI视频创作工具

下载
  • keyMapper (str -> str.substring(0, str.indexOf(','))):通过查找第一个逗号的位置来截取字符串的第一个字段,将其作为Map的键。
  • valueMapper (Function.identity()):将原始的完整字符串作为Map的值。
  • mergeFunction ((existing, replacement) -> existing):这是处理重复键的关键。当遇到相同的键时,此函数决定保留哪个值。此处我们选择保留existing(即Map中已有的值),这意味着对于相同的公司ID,我们总是保留第一次出现的完整行。

2. 引入领域对象提升代码可维护性与可读性

尽管直接处理字符串列表对于简单场景很有效,但在更复杂的应用中,将文本数据解析成具体的领域对象(如Company类)会大大提高代码的可读性、可维护性和类型安全性。

创建领域对象

首先,定义一个POJO(Plain Old Java Object)来表示文本文件中的每一行数据。为了简洁,这里使用Lombok的@Getter和@Builder注解,但也可以使用标准的Java Getter和构造器。

import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class Company {
    private long id;
    private String street;
    private String locationDescription;
    private String companyName;
    private String state;
    private String country;
    private String zipCode;

    // 静态方法,用于将字符串行解析为Company对象
    public static Company parse(String line) {
        String[] arr = line.split(",");
        if (arr.length < 7) {
            throw new IllegalArgumentException("Invalid line format: " + line);
        }
        return Company.builder()
            .id(Long.parseLong(arr[0]))
            .street(arr[1]) // 假设所有字段都有用
            .locationDescription(arr[2])
            .companyName(arr[3])
            .state(arr[4])
            .country(arr[5])
            .zipCode(arr[6])
            .build();
    }

    @Override
    public String toString() {
        return id + "," + street + "," + locationDescription + "," + companyName + "," + state + "," + country + "," + zipCode;
    }
}

使用领域对象进行去重

有了Company对象后,我们可以先将字符串列表映射成Company对象的流,然后再使用Collectors.toMap()进行去重。

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class CompanyDuplicateRemover {

    public static void main(String[] args) {
        List<String> sourceList = List.of(
            "123456,greenwitch street,near dominos store,Opp sandwitch company,Neyork,US,876890",
            "123480,Postwitch street,near KFC store,Opp masala company,Newyork,US,876891",
            "123456,Newyork street,near 100th avenue,King master company,Texas,US,10005"
        );

        List<Company> uniqueCompanies = sourceList.stream()
            .map(Company::parse)                           // 将每行字符串解析为Company对象
            .collect(Collectors.toMap(
                Company::getId,                            // keyMapper: 使用Company对象的ID作为键
                Function.identity(),                       // valueMapper: Company对象本身作为值
                (existing, replacement) -> existing        // mergeFunction: 当ID重复时,保留已存在的Company对象
            ))
            .values()                                      // 获取Map中所有的Company对象
            .stream()
            .toList();                                     // 转换为List

        System.out.println("去重后的Company对象列表:");
        uniqueCompanies.forEach(System.out::println);
        // 预期输出 (Company的toString方法):
        // 123456,greenwitch street,near dominos store,Opp sandwitch company,Neyork,US,876890
        // 123480,Postwitch street,near KFC store,Opp masala company,Newyork,US,876891
    }
}

这种方法的核心优势在于:

  • 类型安全:操作的是强类型对象,而非原始字符串。
  • 可读性强:代码意图更清晰,Company::getId比str -> str.substring(0, str.indexOf(','))更直观。
  • 易于扩展:如果未来需要对公司数据进行更多操作(如修改、筛选),直接操作对象比字符串解析更方便。

注意事项与最佳实践

  1. 文件读取与写入:上述示例是基于内存中的List。在实际应用中,您需要从文本文件读取数据到List,并在去重后将uniqueList或uniqueCompanies写入新的文件或更新原文件。
    // 示例:从文件读取
    // List<String> allLines = Files.readAllLines(Paths.get("your_file.txt"), StandardCharsets.UTF_8);
    //
    // 示例:写入文件
    // Files.write(Paths.get("unique_records.txt"), uniqueList, StandardCharsets.UTF_8);
  2. 错误处理
    • str.indexOf(',')如果字符串中没有逗号,会返回-1,导致substring抛出IndexOutOfBoundsException。
    • Long.parseLong(arr[0])如果字符串不是有效的数字,会抛出NumberFormatException。
    • Company.parse方法中需要考虑数组越界问题(arr.length)。 在生产代码中,应加入适当的try-catch块或使用Optional来处理这些潜在的异常情况。
  3. mergeFunction的选择
    • (existing, replacement) -> existing:保留第一个遇到的记录。
    • (existing, replacement) -> replacement:保留最后一个遇到的记录。 您的业务逻辑将决定应该保留哪个重复项。
  4. 性能考量:对于非常大的文件(GB级别),将所有内容一次性加载到内存中可能会导致内存溢出。在这种情况下,可以考虑以下策略:
    • 逐行处理:使用BufferedReader逐行读取,并将去重逻辑(如使用HashSet存储已处理的ID)应用于每一行。
    • 外部排序/归并:对于无法完全加载到内存的数据,可以考虑使用外部排序算法,将数据分块处理,排序后相邻的重复行会在一起,方便删除。
    • 数据库:如果数据量巨大且需要复杂的查询,将数据导入数据库可能是更好的选择,利用数据库的唯一索引和去重功能。
  5. distinct()与toMap()的区别
    • stream().distinct()方法是基于对象的equals()和hashCode()方法来判断重复的。如果您的需求是判断整个字符串对象是否完全相同,那么distinct()是合适的。
    • 但本教程的需求是根据部分字段(首字段)来判断重复,此时distinct()就不适用,因为即使首字段相同,如果其他字段不同,equals()方法仍会认为它们是不同的对象。因此,Collectors.toMap()是解决这类问题的正确选择。

总结

本文介绍了在Java中根据文本数据首字段删除重复行的两种主要方法:直接使用Stream API的Collectors.toMap()处理字符串,以及通过引入领域对象来提高代码质量。选择哪种方法取决于您的具体需求:对于简单的、一次性的任务,直接处理字符串可能更快捷;而对于需要长期维护、业务逻辑复杂的应用,使用领域对象无疑是更健壮、更可维护的方案。无论选择哪种方法,都应充分考虑错误处理、性能优化和业务逻辑中对重复项的保留策略。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

990

2023.08.02

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

209

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

321

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

292

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

177

2025.08.07

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

739

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

220

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1564

2023.10.24

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

24

2026.03.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11万人学习

Java 教程
Java 教程

共578课时 | 79.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号