0

0

Java中基于首字段删除文本文件重复行并生成唯一记录列表

碧海醫心

碧海醫心

发布时间:2025-12-03 22:12:01

|

759人浏览过

|

来源于php中文网

原创

Java中基于首字段删除文本文件重复行并生成唯一记录列表

本教程详细介绍了如何在java中处理文本文件,根据每行记录的首个字段(例如id)删除重复行,并将去重后的数据存储到列表中。文章提供了两种主要方法:一是利用java stream api的`collectors.tomap`结合自定义合并函数直接处理字符串列表;二是建议通过引入领域对象(如`company`类)来封装数据,提高代码的可读性和可维护性,并演示了如何解析字符串到对象并进行去重操作。

在数据处理场景中,我们经常需要从文本文件中读取数据,并根据特定规则删除重复记录。一个常见的需求是,当每行记录由多个字段组成,且我们只关注某个特定字段(例如行首的ID)是否重复时,需要删除整行重复的记录。本文将介绍两种在Java中实现这一目标的高效方法。

方法一:使用 Collectors.toMap 直接处理字符串列表

Java 8引入的Stream API为集合操作提供了强大且简洁的工具。对于根据某个键去重并保留第一条记录的场景,Collectors.toMap 是一个非常合适的选择。

Collectors.toMap 方法通常有三个参数:

  1. keyMapper: 用于从流中的元素提取键的函数。
  2. valueMapper: 用于从流中的元素提取值的函数。
  3. mergeFunction: 当遇到重复键时,用于解决冲突的函数。

在这个场景中,我们需要将每行的第一个逗号分隔的值作为键,整行字符串作为值。当键重复时,我们选择保留第一个遇到的值。

立即学习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);
    }
}

代码解析:

  • str -> str.substring(0, str.indexOf(',')): 这个Lambda表达式作为 keyMapper,它从每行字符串中提取出第一个逗号之前的部分作为唯一标识符(键)。
  • Function.identity(): 作为 valueMapper,表示我们希望Map的值就是原始的字符串本身。
  • (existing, replacement) -> existing: 这是 mergeFunction,它在 Collectors.toMap 遇到相同的键时被调用。existing 是Map中已经存在的与该键关联的值,replacement 是当前尝试插入的新值。我们选择返回 existing,这意味着我们保留了第一个遇到的具有该键的记录,而丢弃了后续的重复记录。
  • .values().stream().toList(): 在去重完成后,我们从生成的Map中取出所有的值(这些值就是去重后的原始字符串),并将其收集成一个新的 List。

方法二:引入领域对象提升代码可维护性

直接操作字符串虽然简洁,但当数据结构复杂或需要对字段进行更多操作时,容易出错且可读性差。更健壮的方法是定义一个领域(Domain)对象来封装每行数据,然后对这些对象进行去重操作。这不仅提高了代码的类型安全性和可读性,也为后续的数据处理提供了便利。

首先,我们定义一个 Company 类来表示文本文件中的每条记录:

Unscreen
Unscreen

AI智能视频背景移除工具

下载
import lombok.Builder;
import lombok.Getter; // 假设使用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]) // 注意:原始问题答案中未包含street,此处根据数据格式补充
            .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;
    }
}

说明:

  • @Builder 和 @Getter 是Lombok注解,它们会自动生成构造器和Getter方法,减少样板代码。如果项目中没有Lombok,需要手动实现这些方法。
  • parse(String line) 静态方法负责将一行字符串解析成 Company 对象。它通过逗号分割字符串,并将各个部分赋值给对应的字段。这里增加了对 street 字段的解析,使其与原始数据格式保持一致。
  • toString() 方法重写是为了方便打印 Company 对象时能看到其内容,或者在需要将对象转回字符串时使用。

接下来,我们使用这个 Company 类来处理数据并去重:

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

// 假设Company类已定义如上
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(company -> System.out.println(company.toString())); // 打印Company对象
    }
}

代码解析:

  • .map(Company::parse): 这是关键一步,它将 String 类型的流转换为 Company 对象的流。
  • Company::getId: 作为 keyMapper,我们现在可以直接通过 Company 对象的 getId() 方法来获取作为键的ID,这比字符串操作更安全、更直观。
  • Function.identity(): 作为 valueMapper,Map中存储的是 Company 对象本身。
  • mergeFunction 的逻辑与方法一相同,只是现在处理的是 Company 对象。

注意事项与最佳实践

  1. 文件I/O处理: 上述示例假定数据已在 List 中。在实际应用中,您需要从文本文件读取数据。可以使用 java.nio.file.Files.lines() 方法来高效地逐行读取文件内容,并直接将其作为Stream的源:

    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.List;
    import java.util.stream.Collectors;
    
    // ... (Company 类和 Collectors.toMap 逻辑)
    
    try (var lines = Files.lines(Paths.get("your_text_file.txt"))) {
        List<Company> uniqueCompanies = lines
            .map(Company::parse)
            .collect(Collectors.toMap(
                Company::getId,
                Function.identity(),
                (existing, replacement) -> existing
            ))
            .values()
            .stream()
            .toList();
        // 处理 uniqueCompanies
    } catch (IOException e) {
        System.err.println("Error reading file: " + e.getMessage());
    } catch (IllegalArgumentException e) {
        System.err.println("Error parsing line: " + e.getMessage());
    }
  2. 错误处理:

    • 在 Company.parse() 方法中,Long.parseLong() 可能会抛出 NumberFormatException,如果ID不是有效的数字。
    • line.split(",") 后的数组长度可能不足,导致 ArrayIndexOutOfBoundsException。
    • str.indexOf(',') 返回 -1 时,substring() 会抛出 StringIndexOutOfBoundsException。 在生产代码中,应捕获这些异常,或在解析前进行严格的数据校验,确保程序的健壮性。
  3. 性能与内存:

    • 对于小到中等大小的文件(例如几GB以内),上述Stream和Map的方法通常效率很高。
    • 对于非常大的文件(例如几十GB甚至更大),将所有数据一次性加载到内存中可能会导致 OutOfMemoryError。在这种情况下,可以考虑:
      • 分批处理(chunking):每次读取固定数量的行进行处理。
      • 使用外部排序或数据库:将数据导入数据库,利用数据库的唯一索引或去重功能。
      • Bloom Filter:对于判断是否存在重复键的场景,可以在内存有限的情况下提供概率性去重。
  4. 选择方法:

    • 如果数据结构简单,且去重后不需要进一步的字段操作,方法一(直接操作字符串)更简洁。
    • 如果数据结构复杂,或者去重后还需要对各个字段进行操作、校验、转换等,强烈建议使用方法二(引入领域对象)。这会大大提高代码的可读性、可维护性和扩展性。

总结

本文详细介绍了在Java中根据文本文件行的首个字段删除重复记录的两种有效策略。通过利用Java Stream API的 Collectors.toMap,我们可以灵活地定义去重规则。对于简单场景,直接操作字符串是快速有效的方案;而对于复杂或需要高可维护性的项目,引入领域对象并结合Stream API进行处理,无疑是更专业和健壮的选择。在实际应用中,务必结合文件I/O、错误处理和性能考量,选择最适合您需求的实现方式。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
string转int
string转int

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

1010

2023.08.02

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

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

210

2023.12.04

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

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

322

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字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

760

2023.08.03

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

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

221

2023.09.04

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

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

1566

2023.10.24

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

3

2026.03.11

热门下载

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

精品课程

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

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.1万人学习

Java 教程
Java 教程

共578课时 | 80.7万人学习

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

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