讲师中心 微信公众号
AI工具推荐 视频效率加速

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

阿瑶吖_8048

阿瑶吖_8048

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

|

831人浏览过

|

来源于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
    }
}

在上述代码中:

  • 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工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

相关专题

更多
string转int
string转int

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

6659

2023.08.02

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

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

15480

2023.12.04

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

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

1143

2024.02.23

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

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

8590

2025.06.11

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

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

3402

2025.08.07

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

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

1798

2023.08.03

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

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

2804

2023.09.04

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

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

6164

2023.10.24

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

60

2026.10.22

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn