0

0

Java中基于关联键移除列表对象教程

霞舞

霞舞

发布时间:2025-11-02 15:31:21

|

662人浏览过

|

来源于php中文网

原创

Java中基于关联键移除列表对象教程

本教程详细介绍了如何在java中高效地从一个对象列表中移除那些其特定键值在另一个对象列表中不存在的项。文章涵盖了java 8及更高版本中利用stream api的现代化解决方案,以及java 8之前版本通过传统循环和迭代器实现的方法,并对不同方法的性能和适用场景进行了深入分析,旨在帮助开发者选择最优化策略。

1. 定义数据模型

首先,我们定义两个用于示例的Java类,它们分别代表不同类型的数据实体。

public class RetailerExcelConversionDto {
    private String retailerCode; // 零售商编码
    private Integer isActive;    // 是否活跃

    // 构造函数、Getter和Setter
    public RetailerExcelConversionDto(String retailerCode, Integer isActive) {
        this.retailerCode = retailerCode;
        this.isActive = isActive;
    }

    public String getRetailerCode() {
        return retailerCode;
    }

    public void setRetailerCode(String retailerCode) {
        this.retailerCode = retailerCode;
    }

    public Integer getIsActive() {
        return isActive;
    }

    public void setIsActive(Integer isActive) {
        this.isActive = isActive;
    }
}

public class RetailerDto {
    private String code; // 零售商编码
    private Integer age; // 年龄
    private String name; // 名称

    // 构造函数、Getter和Setter
    public RetailerDto(String code, Integer age, String name) {
        this.code = code;
        this.age = age;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

假设我们有以下两个列表实例:

List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
// 填充数据...
retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));

List<RetailerDto> retailerDtoList = new ArrayList<>();
// 填充数据...
retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));

2. 核心问题阐述

我们的目标是从 retailerConversionDtoList 中移除所有 retailerCode 不存在于 retailerDtoList 中任何 code 的项。简而言之,我们希望保留 retailerConversionDtoList 中与 retailerDtoList 存在关联(通过编码匹配)的项。

3. 解决方案一:使用 Java 8 Stream API (推荐)

在 Java 8 及更高版本中,Stream API 提供了一种简洁且高效的方式来处理集合数据。这种方法利用 Set 的快速查找特性,优化了移除操作。

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

实现步骤:

靠岸学术
靠岸学术

一款集翻译,阅读,文献管理于一体的英文文献阅读器

下载
  1. 从 retailerDtoList 中提取所有 code 属性,并将它们收集到一个 Set<String> 中。使用 Set 的目的是为了实现 O(1) 的平均查找时间复杂度,而非 List 的 O(N),这在数据量较大时能显著提升性能。
  2. 使用 Stream API 过滤 retailerConversionDtoList。对于 retailerConversionDtoList 中的每个元素,检查其 retailerCode 是否存在于第一步创建的 Set 中。
  3. 将过滤后的元素收集到一个新的 List 中。

示例代码:

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashSet; // 导入 HashSet 以便构建示例数据

// 假设 RetailerExcelConversionDto 和 RetailerDto 类已定义

public class ListRemovalWithStream {

    public static void main(String[] args) {
        // 示例数据
        List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));
        System.out.println("原始 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));

        List<RetailerDto> retailerDtoList = new ArrayList<>();
        retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
        retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
        System.out.println("参照 retailerDtoList codes: " + retailerDtoList.stream().map(RetailerDto::getCode).collect(Collectors.toList()));

        // 步骤 1: 提取所有存在的 retailer codes 到一个 Set 中
        Set<String> retailerCodes = retailerDtoList.stream()
                                                    .map(RetailerDto::getCode)
                                                    .collect(Collectors.toSet());

        // 步骤 2 & 3: 过滤 retailerConversionDtoList
        retailerConversionDtoList = retailerConversionDtoList.stream()
                                                            .filter(t -> retailerCodes.contains(t.getRetailerCode()))
                                                            .collect(Collectors.toList());

        System.out.println("过滤后的 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));
        // 预期输出: [R001]
    }
}

优点:

  • 代码简洁: Stream API 提供了声明式编程风格,使代码更易读、更简洁。
  • 效率高: 利用 Set 进行查找,时间复杂度接近 O(N + M),其中 N 和 M 分别是两个列表的大小。
  • 函数式编程: 更好地支持函数式编程范式,易于并行化(尽管在此场景下可能不需要)。

4. 解决方案二:Java 8 之前的传统方法

对于不支持 Java 8 Stream API 的旧版本 Java 环境,我们可以通过传统的循环和迭代器来实现相同的功能。

4.1 方法一:构建新列表

这种方法与 Stream API 的逻辑类似,都是先构建一个用于查找的 Set,然后遍历原列表,将符合条件的元素添加到一个新列表中。

实现步骤:

  1. 创建一个 HashSet<String> 用于存储 retailerDtoList 中所有 code。
  2. 遍历 retailerDtoList,将每个 RetailerDto 对象的 code 添加到 HashSet 中。
  3. 创建一个新的 List<RetailerExcelConversionDto> 来存储过滤后的结果。
  4. 遍历 retailerConversionDtoList。对于每个元素,检查其 retailerCode 是否存在于 HashSet 中。如果存在,则将其添加到新列表中。

示例代码:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ListRemovalTraditionalNewList {

    public static void main(String[] args) {
        // 示例数据 (同上)
        List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));
        System.out.println("原始 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));

        List<RetailerDto> retailerDtoList = new ArrayList<>();
        retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
        retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
        System.out.println("参照 retailerDtoList codes: " + retailerDtoList.stream().map(RetailerDto::getCode).collect(Collectors.toList()));

        // 步骤 1 & 2: 提取所有存在的 retailer codes 到一个 Set 中
        Set<String> retailerCodes = new HashSet<>();
        for (RetailerDto retailer : retailerDtoList) {
            retailerCodes.add(retailer.getCode());
        }

        // 步骤 3 & 4: 遍历并构建新列表
        List<RetailerExcelConversionDto> newRetailerConversionDtoList = new ArrayList<>();
        for (RetailerExcelConversionDto item : retailerConversionDtoList) {
            if (retailerCodes.contains(item.getRetailerCode())) {
                newRetailerConversionDtoList.add(item);
            }
        }
        retailerConversionDtoList = newRetailerConversionDtoList; // 更新引用到新列表

        System.out.println("过滤后的 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));
        // 预期输出: [R001]
    }
}

优点:

  • 兼容性好: 适用于所有 Java 版本。
  • 逻辑清晰: 循环和条件判断直观易懂。

缺点:

  • 内存开销: 需要创建一个新的列表,如果原始列表很大,可能会有额外的内存开销。
  • 代码冗长: 相较于 Stream API,代码量更多。

4.2 方法二:使用迭代器原地移除

如果需要直接修改原始列表而不是创建新列表,可以使用迭代器(Iterator)的 remove() 方法。注意:在循环遍历集合时,直接使用增强for循环(for-each)或普通 for 循环通过索引移除元素会导致 ConcurrentModificationException 或跳过元素。 迭代器的 remove() 方法是唯一安全的在遍历时修改集合的方式。

实现步骤:

  1. 创建一个 HashSet<String> 用于存储 retailerDtoList 中所有 code。
  2. 获取 retailerConversionDtoList 的迭代器。
  3. 使用 while (iterator.hasNext()) 循环遍历列表。
  4. 在循环中,获取当前元素,并检查其 retailerCode 是否存在于 HashSet 中。
  5. 如果 retailerCode 不存在于 HashSet 中,则使用 iterator.remove() 方法将当前元素从列表中移除。

示例代码:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class ListRemovalTraditionalIterator {

    public static void main(String[] args) {
        // 示例数据 (同上)
        List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));
        System.out.println("原始 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));

        List<RetailerDto> retailerDtoList = new ArrayList<>();
        retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
        retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
        System.out.println("参照 retailerDtoList codes: " + retailerDtoList.stream().map(RetailerDto::getCode).collect(Collectors.toList()));

        // 步骤 1: 提取所有存在的 retailer codes 到一个 Set 中
        Set<String> retailerCodes = new HashSet<>();
        for (RetailerDto retailer : retailerDtoList) {
            retailerCodes.add(retailer.getCode());
        }

        // 步骤 2-5: 使用迭代器原地移除
        for (Iterator<RetailerExcelConversionDto> it = retailerConversionDtoList.iterator(); it.hasNext(); ) {
            RetailerExcelConversionDto next = it.next();
            if (!retailerCodes.contains(next.getRetailerCode())) {
                it.remove(); // 如果不存在,则移除
            }
        }

        System.out.println("过滤后的 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));
        // 预期输出: [R001]
    }
}

优点:

  • 节省内存: 直接修改原始列表,无需创建新列表。
  • 兼容性好: 适用于所有 Java 版本。

缺点:

  • 代码相对复杂: 迭代器操作不如 Stream API 直观。
  • 易错性: 如果不正确使用迭代器(例如,在增强for循环中尝试修改),很容易导致运行时错误。

5. 性能考量与最佳实践

  • 使用 Set 进行查找: 无论采用哪种方法,将参照列表的键值收集到 HashSet 中都是至关重要的优化步骤。HashSet 提供了平均 O(1) 的查找时间复杂度,而 ArrayList 或 LinkedList 的 contains() 方法是 O(N),这将极大地影响整体性能,尤其是在列表数据量较大时。
  • Java 8+ 优先选择 Stream API: Stream API 结合 filter 和 collect 提供了最简洁、可读性高且性能优越的解决方案。它避免了手动管理迭代器,并且在内部实现上通常经过优化。
  • 旧版本 Java 的选择:
    • 如果内存不是瓶颈,并且希望代码更直观,可以选择“构建新列表”的方法。
    • 如果对内存使用有严格要求,且愿意处理迭代器逻辑,可以选择“使用迭代器原地移除”的方法。务必正确使用 Iterator.remove()。

6. 总结

本文详细介绍了在 Java 中根据关联键从一个对象列表中移除项的多种方法。对于 Java 8 及更高版本,推荐使用 Stream API 结合 Set 进行高效过滤,它提供了最佳的简洁性和性能平衡。对于旧版本 Java,通过将参照键存储在 HashSet 中,然后选择构建新列表或使用迭代器原地移除,也能实现相同的目标。理解不同方法的优缺点和适用场景,有助于开发者在实际项目中做出明智的选择,编写出更健壮、高效的代码。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
string转int
string转int

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

1051

2023.08.02

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

107

2023.09.25

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

25

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

44

2026.03.12

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

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

177

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

50

2026.03.10

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

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

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

热门下载

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

精品课程

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

共162课时 | 21.4万人学习

成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.6万人学习

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

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