Java中根据关联键值高效过滤列表元素教程

Java中根据关联键值高效过滤列表元素教程

本教程详细介绍了如何在java中高效地从一个对象列表中移除那些其特定键值不存在于另一个对象列表中的元素。文章将分别探讨java 8及更高版本中利用stream api和set进行优化的解决方案,以及java 8之前版本通过传统循环和迭代器实现相同功能的策略,并强调了性能优化和代码可读性的关键点。

在Java开发中,我们经常会遇到需要根据一个列表的特定属性,来筛选或修改另一个列表的需求。例如,给定两个包含不同类型对象的列表,我们可能需要从第一个列表中移除所有其某个关键属性值在第二个列表中不存在的元素。本文将以具体示例深入探讨如何高效地实现这一目标。

假设我们有以下两个数据传输对象(DTO)类:

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 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;    }}

并且我们拥有这两个类的实例列表:

List retailerConversionDtoList = getAllRetailerConversionDtoList();List retailerDtoList = getAllRetailer();

我们的目标是从 retailerConversionDtoList 中移除所有 retailerCode 属性值在 retailerDtoList 的 code 属性值中不存在的元素。

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

1. 现代Java (Java 8+) 的解决方案:利用Stream API

Java 8引入的Stream API提供了一种声明式、函数式的方式来处理集合数据,极大地简化了代码并提高了可读性。结合 Set 的高效查找特性,我们可以以非常优化的方式解决这个问题。

核心思路:

从 retailerDtoList 中提取所有 code 属性值,并将它们收集到一个 Set 中。Set 提供了平均 O(1) 的查找时间复杂度,这对于后续的过滤操作至关重要。对 retailerConversionDtoList 使用Stream API进行过滤,只保留那些 retailerCode 存在于之前构建的 Set 中的元素。

示例代码:

import java.util.List;import java.util.Set;import java.util.stream.Collectors;import java.util.Arrays; // 仅用于示例数据// 假设 getAllRetailerConversionDtoList() 和 getAllRetailer() 已经实现// 模拟数据List retailerConversionDtoList = Arrays.asList(    new RetailerExcelConversionDto("R001", 1),    new RetailerExcelConversionDto("R002", 0),    new RetailerExcelConversionDto("R003", 1),    new RetailerExcelConversionDto("R005", 1) // R005 不存在于 retailerDtoList);List retailerDtoList = Arrays.asList(    new RetailerDto("R001", 30, "Retailer A"),    new RetailerDto("R002", 25, "Retailer B"),    new RetailerDto("R003", 35, "Retailer C"),    new RetailerDto("R004", 40, "Retailer D"));// 步骤1: 提取 retailerDtoList 中的所有 code 到一个 SetSet retailerCodes = retailerDtoList.stream()                                         .map(RetailerDto::getCode) // 或者 t -> t.getCode()                                         .collect(Collectors.toSet());// 步骤2: 过滤 retailerConversionDtoList,只保留 retailerCode 存在于 retailerCodes Set 中的元素retailerConversionDtoList = retailerConversionDtoList.stream()                                                    .filter(t -> retailerCodes.contains(t.getRetailerCode())) // 或者 RetailerExcelConversionDto::getRetailerCode                                                    .collect(Collectors.toList());// 此时 retailerConversionDtoList 将只包含 R001, R002, R003retailerConversionDtoList.forEach(r -> System.out.println(r.getRetailerCode()));// 预期输出: R001, R002, R003

这种方法利用了 Set 的高效查找能力(平均 O(1)),使得整个过滤过程的时间复杂度接近 O(N + M),其中 N 是 retailerConversionDtoList 的大小,M 是 retailerDtoList 的大小。相比于嵌套循环的 O(N*M) 复杂度,这是一个显著的性能提升。

GAIPPT GAIPPT

AI PPT制作和美化神器

GAIPPT 1215 查看详情 GAIPPT

2. 传统Java (Java 8之前) 的解决方案

在Java 8之前的版本中,由于没有Stream API,我们需要通过传统的循环和集合操作来实现相同的功能。同样,利用 Set 进行查找仍然是优化性能的关键。

核心思路:

首先,遍历 retailerDtoList,将其所有 code 属性值收集到一个 HashSet 中。然后,根据需求选择以下两种方式之一来过滤 retailerConversionDtoList。

2.1. 构建一个新的列表

这种方法是创建并返回一个符合条件的新列表,原始列表保持不变。

示例代码:

import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;import java.util.Arrays; // 仅用于示例数据// 模拟数据List retailerConversionDtoList = Arrays.asList(    new RetailerExcelConversionDto("R001", 1),    new RetailerExcelConversionDto("R002", 0),    new RetailerExcelConversionDto("R003", 1),    new RetailerExcelConversionDto("R005", 1));List retailerDtoList = Arrays.asList(    new RetailerDto("R001", 30, "Retailer A"),    new RetailerDto("R002", 25, "Retailer B"),    new RetailerDto("R003", 35, "Retailer C"),    new RetailerDto("R004", 40, "Retailer D"));// 步骤1: 提取 retailerDtoList 中的所有 code 到一个 SetSet retailerCodes = new HashSet();for (RetailerDto retailer : retailerDtoList) {    retailerCodes.add(retailer.getCode());}// 步骤2: 遍历 retailerConversionDtoList,将符合条件的元素添加到新列表List newRetailerConversionDtoList = new ArrayList();for (RetailerExcelConversionDto item : retailerConversionDtoList) {    if (retailerCodes.contains(item.getRetailerCode())) {        newRetailerConversionDtoList.add(item);    }}// 现在 newRetailerConversionDtoList 包含过滤后的元素newRetailerConversionDtoList.forEach(r -> System.out.println(r.getRetailerCode()));// 预期输出: R001, R002, R003

2.2. 使用迭代器在原列表上移除元素

如果需要直接修改原始列表而不是创建新列表,那么在循环中移除元素时必须使用 Iterator。直接在增强型 for 循环(for-each loop)中调用 list.remove() 会导致 ConcurrentModificationException。

示例代码:

import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;import java.util.Arrays; // 仅用于示例数据// 模拟数据List retailerConversionDtoList = new ArrayList(Arrays.asList( // 注意这里需要可变列表    new RetailerExcelConversionDto("R001", 1),    new RetailerExcelConversionDto("R002", 0),    new RetailerExcelConversionDto("R003", 1),    new RetailerExcelConversionDto("R005", 1)));List retailerDtoList = Arrays.asList(    new RetailerDto("R001", 30, "Retailer A"),    new RetailerDto("R002", 25, "Retailer B"),    new RetailerDto("R003", 35, "Retailer C"),    new RetailerDto("R004", 40, "Retailer D"));// 步骤1: 提取 retailerDtoList 中的所有 code 到一个 SetSet retailerCodes = new HashSet();for (RetailerDto retailer : retailerDtoList) {    retailerCodes.add(retailer.getCode());}// 步骤2: 使用迭代器遍历 retailerConversionDtoList 并移除不符合条件的元素Iterator iterator = retailerConversionDtoList.iterator();while (iterator.hasNext()) {    RetailerExcelConversionDto item = iterator.next();    if (!retailerCodes.contains(item.getRetailerCode())) {        iterator.remove(); // 使用迭代器安全地移除元素    }}// 现在 retailerConversionDtoList 已经被修改retailerConversionDtoList.forEach(r -> System.out.println(r.getRetailerCode()));// 预期输出: R001, R002, R003

3. 注意事项与性能考量

使用 Set 进行查找: 无论是Java 8+还是Java 8之前的版本,将用于比较的键值集合存储在 HashSet 中是实现高效过滤的关键。HashSet 提供了平均 O(1) 的查找时间复杂度,而 ArrayList 的 contains() 方法是 O(N),这在处理大量数据时会导致巨大的性能差异。Stream API 的优势: Java 8的Stream API不仅代码更简洁、可读性更高,而且在某些情况下(如并行流)还能提供性能上的优势。它鼓励函数式编程范式,使得数据转换和过滤操作更加流畅。修改列表时的选择:如果可以接受创建一个新列表,那么构建新列表(如Stream API的 collect(Collectors.toList()) 或传统方式中的 new ArrayList())通常是更安全、更简洁的选择。如果必须在原地修改原始列表,并且是Java 8之前的版本,则必须使用 Iterator 的 remove() 方法。Java 8+ 可以使用 List.removeIf() 方法,它内部也是通过迭代器实现的,但提供了更简洁的语法。例如,在Java 8+中,原地修改也可以这样实现:

retailerConversionDtoList.removeIf(item -> !retailerCodes.contains(item.getRetailerCode()));

这种方式比手动使用 Iterator 更简洁。

对象属性的访问: 确保你的DTO类有相应的getter方法,或者如果属性是public的,可以直接访问。在Stream API中,可以使用方法引用(如 RetailerDto::getCode)或Lambda表达式(如 t -> t.getCode())。

总结

高效地从一个列表中移除基于另一个列表的关联键值不存在的元素,关键在于利用 Set 的快速查找能力。对于Java 8及更高版本,Stream API提供了极其简洁和强大的解决方案。而在Java 8之前,虽然需要更多手动循环,但通过将查找键预先加载到 HashSet 中,依然可以实现高性能的数据过滤。选择哪种方法取决于你的Java版本和对代码风格、可变性需求的偏好。

以上就是Java中根据关联键值高效过滤列表元素教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/764437.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
192.168.1.1路由器设置向导 192.168.1.1 新手快速上网配置教程
上一篇 2025年11月26日 02:24:40
下一篇 2025年11月26日 02:24:48

相关推荐

  • PHP如何实现动态图表_PHP动态图表生成的方法与代码实例

    PHP通过结合前端图表库实现动态图表生成,常用方法包括:1. 使用Chart.js与Ajax获取PHP输出的JSON数据绘制柱状图;2. 利用Google Charts在前端嵌入PHP生成的JSON数据展示折线图;3. 通过ECharts调用PHP接口返回的数据渲染交互式饼图。核心是PHP处理数据并…

    2026年5月10日
    000
  • Python中如何实现Bellman-Ford算法?

    bellman-ford算法在python中可通过多次放松操作实现,用于求解最短路径并检测负权环。1)初始化距离数组,设源点距离为0。2)进行|v|-1次放松操作。3)检测负权环,若存在则抛出异常。该算法在金融网络中应用广泛,但处理大规模图时性能较慢,可考虑优化和并行化。 在Python中实现Bel…

    2026年5月10日
    100
  • 什么是币安人生?如何买入、卖出币安人生操作步骤教程

    币安人生指通过币安平台参与理财项目实现数字资产增值。首先登录账户,进入【资金】-【理财】页面,选择活期或定期产品并点击【申购】,输入金额前需重点关注预期年化收益、计息规则等条款;赎回时进入对应产品详情页,点击【赎回】并输入数量,注意不同产品规则差异及可能的收益损失,确认后资产将退回现货账户。 欧易官…

    2026年5月10日
    000
  • Python中如何通过字符串动态创建对象并调用其方法?

    本文介绍如何在Python中通过字符串动态创建对象并调用其方法,这在需要根据配置或运行时信息灵活处理对象时非常有用。 直接使用字符串无法实现,需要借助Python的反射机制。 核心在于getattr函数,它接收对象和属性名(字符串)作为参数。如果属性存在,则返回属性值;否则,抛出AttributeE…

    2026年5月10日
    000
  • 从 Django 视图传递变量到模板中的 JavaScript 脚本

    在 Django Web 开发中,经常需要在前端 JavaScript 代码中使用后端 Python 代码中的数据。例如,你可能需要根据数据库中的数据动态生成图表,或者根据用户的角色显示不同的界面元素。直接在 JavaScript 中使用 Django 模板变量可能会导致安全问题,并且不够优雅。Dj…

    2026年5月10日
    000
  • 如何使用Golang实现错误处理_使用error类型和自定义错误

    Go错误处理显式依赖error接口,通过errors.New、fmt.Errorf(支持%w包装)和自定义结构体实现;用==、errors.Is、errors.As判断错误,支持错误链与类型提取。 Go 语言的错误处理强调显式判断和传递,不依赖异常机制。核心是使用内置的 error 接口类型,并可通…

    2026年5月10日
    000
  • HTML5网页如何实现拖拽功能 HTML5网页拖放API的详细解析

    首先设置元素draggable=”true”并监听dragstart事件,通过dataTransfer传递数据;然后为目标区域绑定dragover、dragenter和drop事件,其中dragover需调用preventDefault()以允许投放;最后在drop事件中获取…

    2026年5月10日
    000
  • Node.js http.createServer 常见陷阱与正确响应处理

    本文深入探讨了Node.js中使用`http.createServer`时常见的配置错误和响应处理问题。我们将详细讲解如何正确地将请求监听器函数传递给服务器实例,并强调在构建HTTP响应时,确保内容类型(Content-Type)与实际发送的数据(如HTML或JSON)保持一致的重要性,避免发送冲突…

    2026年5月10日
    000
  • Go语言中类型无关函数的实现:接口的应用

    在go语言中,与haskell等语言的hindley-milner类型系统不同,无法直接使用类型变量。go通过空接口`interface{}`来模拟类型无关的函数行为,允许函数处理任何类型的数据,从而实现类似泛型的功能,例如在实现`map`等高阶函数时。这种方式在go引入泛型之前是处理多态性的主要手…

    2026年5月10日
    100
  • Go语言中指针操作符*与取地址符&的全面解析

    本文深入探讨Go语言中*和&这两个核心操作符的作用。&用于获取变量的内存地址,生成一个指向该变量的指针;而*则用于声明指针类型、对指针进行解引用以访问其指向的值,以及通过指针间接修改变量的值。理解它们对于掌握Go的内存管理和数据传递机制至关重要,尤其是在函数参数传递和结构体操作中。 …

    2026年5月10日
    000
  • Electron 渲染进程安全集成 Node.js fs 模块指南

    本教程旨在指导开发者如何在 Electron 渲染进程中安全地使用 Node.js 的 fs 模块,避免启用 nodeIntegration: true 和 contextIsolation: false 等不安全的配置。通过利用 Electron 的 IPC(进程间通信)机制和预加载脚本(prel…

    2026年5月10日
    100
  • C++状态模式如何管理状态 使用有限状态机的实现方法

    C++状态模式如何管理状态 使用有限状态机的实现方法C++状态模式如何管理状态 使用有限状态机的实现方法C++状态模式如何管理状态 使用有限状态机的实现方法C++状态模式如何管理状态 使用有限状态机的实现方法

    有限状态机在c++++中通过定义状态接口、创建具体状态类、实现上下文类和管理状态转换逻辑来实现状态模式。1. 定义状态接口或基类,声明通用方法如handleinput()和getcolor();2. 创建具体状态类,继承接口并实现各自行为;3. 创建上下文类,持有当前状态并处理状态切换;4. 实现状…

    2026年5月10日 用户投稿
    000
  • 如何理解C++中的整数溢出?

    c++++中的整数溢出发生在整数值超过其类型最大值时,会导致程序逻辑错误和安全漏洞。1)使用更大数据类型如long long;2)使用std::numeric_limits检查值范围;3)通过异常处理机制抛出溢出异常。 理解C++中的整数溢出是编程过程中不可或缺的一环,相信许多程序员都曾因整数溢出而…

    2026年5月10日
    000
  • GLTF模型加载纹理缺失:从源头排查与解决指南

    在使用GLTFLoader加载3D模型时,若遇到纹理缺失问题,首要且关键的排查步骤是验证GLTF模型本身的完整性。本教程将指导您如何通过在线工具检查模型纹理,区分模型源文件问题与代码加载问题,并提供相应的解决方案,确保您的3D对象能正确显示纹理。 理解GLTF与纹理加载机制 gltf(gl tran…

    2026年5月10日
    000
  • PowerShell 调用 PHP 网页功能及结果处理

    本教程详细阐述了如何利用 PowerShell 的 Invoke-WebRequest cmdlet 外部调用 PHP 网页,并有效处理其返回结果。内容涵盖了基本的网页请求发送、HTTP 状态码的检查、网页内容的获取以及健壮的异常处理机制,旨在帮助用户实现与远程网页的自动化交互和数据处理。 使用 P…

    2026年5月10日
    000
  • C++的consteval和constinit是什么_C++20中真正的编译期常量初始化

    consteval 强制函数在编译期求值,如 consteval int square(int n) 只能接受编译期常量参数;constinit 确保变量以常量初始化,如 constinit static int x = 42 避免动态初始化,用于解决静态初始化顺序问题。两者分别强化了编译期计算和初…

    2026年5月10日
    000
  • Blazor JS Interop 调用 Geolocation API 教程

    在 Blazor 中调用 Geolocation API 需通过 JS Interop:JavaScript 封装 navigator.geolocation 为 Promise 函数 getLocation,C# 使用 IJSRuntime.InvokeAsync 调用并匹配字段名,同时处理权限拒…

    2026年5月10日
    000
  • 如何精确获取多组单选按钮的最终选中值

    本教程旨在解决前端开发中,如何高效且准确地获取多组单选按钮(如产品变体选项)的最终选中值。我们将探讨在“添加到购物车”等操作触发时,避免中间选择状态干扰,仅捕获用户最终确认选项的最佳实践,并通过JavaScript代码示例详细演示其实现方法,确保数据一致性与用户体验。 场景描述与挑战 在电子商务网站…

    2026年5月10日
    000
  • 如何处理图像EXIF方向并转换为Base64,避免数据丢失

    本教程旨在解决图像EXIF方向信息在转换为Base64编码过程中丢失的问题。通过结合使用piexif库提取并移除EXIF方向数据,以及Jimp库对图像进行实际旋转,我们可以确保生成的Base64图像在视觉上保持正确的方向,从而满足API调用等需求,避免因EXIF元数据丢失而导致的显示错误。 在处理图…

    2026年5月10日
    000
  • html文档中含有java怎么运行_html含java运行方法【教程】

    现代浏览器不支持Java Applet,推荐通过JavaScript调用Java后端服务或使用WebAssembly运行Java代码。 如果您在HTML文档中嵌入了Java代码,但发现无法正常运行,这通常是因为现代浏览器不再支持Java小程序(Applet)或相关插件。以下是几种实现HTML中Jav…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信