Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Java中高效关联父子列表数据的优化策略_创想鸟

Java中高效关联父子列表数据的优化策略

Java中高效关联父子列表数据的优化策略

本文探讨在Java中将子列表数据高效关联到父列表对象的方法。针对常见的遍历父列表并在内部过滤子列表的低效模式,文章提出了一种基于哈希映射(HashMap)的优化方案。通过一次性预处理子列表并将其按父ID分组存储在Map中,后续关联操作的时间复杂度从O(nm)显著降低至O(n+m),从而大幅提升大数据量下的处理性能。

场景描述与初始实现

在业务开发中,我们经常会遇到需要将关联的子项数据集合添加到主对象列表中的场景。例如,我们有一个产品(product)列表,以及一个产品子项(productsub)列表。每个产品子项都通过 productid 关联到特定的产品。我们的目标是将所有属于某个产品的子项收集起来,并设置到该产品的 productsublist 属性中。

以下是相关的类定义:

public class Product {    private long productId;    private String productName;    private BigDecimal costAmount;    private List productSublist;    // 构造函数、Getter和Setter(为简洁省略)    public Product(long productId, String productName, BigDecimal costAmount) {        this.productId = productId;        this.productName = productName;        this.costAmount = costAmount;        this.productSublist = new ArrayList(); // 初始化为空列表    }    public long getProductId() { return productId; }    public void setProductId(long productId) { this.productId = productId; }    public List getProductSublist() { return productSublist; }    public void setProductSublist(List productSublist) { this.productSublist = productSublist; }    // ... 其他getter/setter}public class ProductSub {    private long productId;    private long productSubId;    private String lineItemName;    private BigDecimal subCost;    // 构造函数、Getter和Setter(为简洁省略)    public ProductSub(long productId, long productSubId, String lineItemName, BigDecimal subCost) {        this.productId = productId;        this.productSubId = productSubId;        this.lineItemName = lineItemName;        this.subCost = subCost;    }    public long getProductId() { return productId; }    public void setProductId(long productId) { this.productId = productId; }    // ... 其他getter/setter}

一个常见的、直观的实现方式是遍历 Product 列表,对于每一个 Product 对象,再遍历或过滤 ProductSub 列表,找出所有匹配的子项,然后将其设置到 Product 对象中。

List productList = /* 从服务获取 Product 列表 */;List productSubList = /* 从服务获取 ProductSub 列表 */;// 初始实现方式for (Product productItem : productList){    List productSubItems = productSubList.stream()       .filter(x -> x.getProductId() == productItem.getProductId())       .collect(Collectors.toList());    productItem.setProductSubList(productSubItems);}

初始实现的问题分析

上述初始实现虽然逻辑清晰,但在性能上存在明显的瓶颈。假设 productList 的大小为 n,productSubList 的大小为 m。在每次外层循环中,我们都会对 productSubList 进行一次完整的流式过滤和收集操作。这意味着,对于每一个 Product,我们都需要遍历(或部分遍历,取决于过滤器的实现)m 个 ProductSub 对象。

因此,这种方法的整体时间复杂度为 *O(n m)**。当 n 和 m 都较大时,例如各有数千甚至数万条记录,这种平方级别的复杂度会导致非常显著的性能下降,甚至可能造成应用程序响应缓慢或内存溢出。

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

优化的解决方案:使用哈希映射

为了提高效率,我们可以利用哈希映射(HashMap)的O(1)平均时间复杂度查找特性。核心思想是:首先对 productSubList 进行一次预处理,将其中的 ProductSub 对象按照 productId 分组存储到一个 Map 中。这样,每个 productId 都会对应一个 ProductSub 列表。之后,我们只需遍历 productList 一次,通过 productId 从 Map 中直接获取对应的 ProductSub 列表即可。

这种方法的步骤如下:

序列猴子开放平台 序列猴子开放平台

具有长序列、多模态、单模型、大数据等特点的超大规模语言模型

序列猴子开放平台 0 查看详情 序列猴子开放平台 创建一个 Map<Long, List>,其中键是 productId,值是该 productId 下所有 ProductSub 对象的列表。遍历 productSubList,将每个 ProductSub 对象添加到其对应 productId 的列表中,并存储在 Map 中。遍历 productList,对于每个 Product 对象,使用其 productId 从 Map 中查找对应的 ProductSub 列表,并将其设置到 Product 对象的 productSublist 属性中。

以下是优化后的代码示例:

import java.math.BigDecimal;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.stream.Collectors;public class ProductListAssociator {    public static void associateProductSublists(List productList, List productSubList) {        // 步骤1 & 2: 预处理 ProductSub 列表,将其按 productId 分组到 Map 中        Map<Long, List> productSubMap = new HashMap();        for (ProductSub productSub : productSubList) {            // 使用 computeIfAbsent 确保如果键不存在,则创建一个新的 ArrayList            productSubMap.computeIfAbsent(productSub.getProductId(), k -> new ArrayList()).add(productSub);        }        // 步骤3: 遍历 Product 列表,从 Map 中获取对应的子列表并设置        for (Product product : productList) {            // 从 Map 中获取子列表,如果不存在则返回空列表,避免 NPE            List associatedSubList = productSubMap.getOrDefault(product.getProductId(), new ArrayList());            product.setProductSubList(associatedSubList);        }    }    // 示例用法    public static void main(String[] args) {        // 模拟数据        List products = new ArrayList();        products.add(new Product(1L, "Laptop", new BigDecimal("1200.00")));        products.add(new Product(2L, "Mouse", new BigDecimal("25.00")));        products.add(new Product(3L, "Keyboard", new BigDecimal("75.00")));        List productSubs = new ArrayList();        productSubs.add(new ProductSub(1L, 101L, "CPU i7", new BigDecimal("400.00")));        productSubs.add(new ProductSub(1L, 102L, "RAM 16GB", new BigDecimal("100.00")));        productSubs.add(new ProductSub(2L, 201L, "Wireless Mouse", new BigDecimal("20.00")));        productSubs.add(new ProductSub(1L, 103L, "SSD 512GB", new BigDecimal("80.00")));        productSubs.add(new ProductSub(3L, 301L, "Mechanical Keyboard", new BigDecimal("70.00")));        System.out.println("--- 关联前 ---");        products.forEach(p -> System.out.println("Product ID: " + p.getProductId() + ", Sublist Size: " + p.getProductSublist().size()));        associateProductSublists(products, productSubs);        System.out.println("n--- 关联后 ---");        products.forEach(p -> {            System.out.println("Product ID: " + p.getProductId() + ", Sublist Size: " + p.getProductSublist().size());            p.getProductSublist().forEach(sub -> System.out.println("  - Sub ID: " + sub.getProductSubId() + ", Name: " + sub.getLineItemName()));        });    }}

性能效益分析

优化后的方法,其时间复杂度显著降低。

构建 productSubMap 的过程需要遍历 productSubList 一次,时间复杂度为 O(m)。遍历 productList 并从 Map 中获取子列表的过程需要遍历 productList 一次,每次 Map 查找的平均时间复杂度为 O(1),所以这部分的时间复杂度为 O(n)

因此,整体的时间复杂度为 O(n + m)。与初始的 O(n * m) 相比,这是一个巨大的改进,尤其是在 n 和 m 值较大的情况下。例如,如果 n=1000, m=1000,初始方法需要大约 1,000,000 次操作,而优化方法只需要大约 2,000 次操作。

注意事项与最佳实践

空子列表处理:在从 Map 中获取子列表时,如果某个 productId 在 productSubMap 中不存在(即该产品没有任何子项),map.get(productId) 将返回 null。为了避免 NullPointerException,可以使用 map.getOrDefault(product.getProductId(), new ArrayList()) 来确保总是返回一个非空的 List,即使是空列表。内存消耗:虽然哈希映射方案在时间复杂度上更优,但它需要额外的内存来存储 productSubMap。对于极大规模的 productSubList,这可能会是一个考虑因素。然而,在大多数实际应用中,这种内存开销通常是可接受的,并且其带来的性能提升远超内存成本。Java 8 Stream API 的 Collectors.groupingBy:对于构建 productSubMap,Java 8 提供了更简洁的 Stream API 方式:

Map<Long, List> productSubMap = productSubList.stream()        .collect(Collectors.groupingBy(ProductSub::getProductId));

这种方式同样能达到 O(m) 的时间复杂度,并且代码更具声明性。

线程安全:如果 productList 和 productSubList 是在多线程环境下共享并修改的,需要考虑线程安全问题,例如使用并发集合或适当的同步机制。但在上述单次关联操作中,如果源列表是不可变的或仅在当前方法作用域内使用,则无需额外处理。

总结

在Java中处理父子列表关联的场景时,选择合适的算法和数据结构至关重要。直接的嵌套循环或在循环内部进行过滤操作,虽然直观,但其 O(n * m) 的时间复杂度在数据量较大时会成为性能瓶颈。通过引入哈希映射进行预处理,将子列表按关联ID分组,我们可以将时间复杂度优化到 O(n + m),从而实现更高效、更具扩展性的数据关联操作。这种模式不仅适用于产品与子项的场景,也广泛适用于其他一对多关系的数据集合关联。

以上就是Java中高效关联父子列表数据的优化策略的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何学习ThinkPHP?途径和建议分享
上一篇 2025年11月5日 14:26:40
摆脱编码器依赖!Encoder-free 3D 多模态大模型,性能超越 13B 现有 SOTA
下一篇 2025年11月5日 14:26:51

相关推荐

  • mysql如何分析索引使用 mysql创建索引后的执行计划解读

    mysql如何分析索引使用 mysql创建索引后的执行计划解读mysql如何分析索引使用 mysql创建索引后的执行计划解读mysql如何分析索引使用 mysql创建索引后的执行计划解读mysql如何分析索引使用 mysql创建索引后的执行计划解读

    要分析mysql索引使用和执行计划,核心是通过explain命令查看查询路径,并结合handler_read%状态变量评估索引效率。1. 使用explain命令分析执行计划,关注type、key、extra等列,判断是否高效利用索引;2. 通过show global status like &#82…

    2026年9月22日 用户投稿
    000
  • mysql怎么添加前缀索引 mysql创建前缀索引的长度选择

    mysql怎么添加前缀索引 mysql创建前缀索引的长度选择mysql怎么添加前缀索引 mysql创建前缀索引的长度选择mysql怎么添加前缀索引 mysql创建前缀索引的长度选择mysql怎么添加前缀索引 mysql创建前缀索引的长度选择

    在mysql中,为长字符串列添加前缀索引的核心目的是优化查询性能并节省存储空间。1. 前缀索引通过仅索引列值的前n个字符实现这一目标;2. 前缀长度的选择需在区分度与存储效率之间取得平衡,理想长度应确保高区分度(如90%以上)且不过度冗余;3. 可通过执行select count(distinct …

    2026年9月22日 用户投稿
    000
  • 《植物大战僵尸:重植版》制作人:价格亲民 未使用AI!

    经典塔防游戏《植物大战僵尸》在问世16年后迎来重磅回归。由PopCap Games精心打造的重制作品——《植物大战僵尸:重植版》将于10月23日正式登陆PlayStation、Xbox、Nintendo Switch以及PC平台。 据The Gamer报道,该游戏执行制作人Jake Neri在采访中…

    2026年9月22日
    200
  • VSCode运行多文件C项目 完整VSCode配置C++开发教程

    要解决#%#$#%@%@%$#%$#%#%#$%@_e2fc++805085e25c9761616c00e065bfe8运行多文件c项目的问题,核心是正确配置tasks.json、launch.json和settings.json文件以定义编译、调试和项目路径。首先安装c/c++扩展插件和可选的编译…

    2026年9月22日
    000
  • Java集合框架在实际项目中的最佳实践

    合理选择集合类型并预设容量,使用不可变集合保护数据,避免遍历中修改结构,可提升Java程序性能与安全性。 Java集合框架是开发中使用最频繁的工具之一,合理使用能显著提升代码的可读性、性能和稳定性。在实际项目中,遵循一些最佳实践可以避免常见陷阱,提高程序健壮性。 选择合适的集合类型 不同场景应选用最…

    2026年9月22日
    000
  • 全球首发天玑9500!vivo X300发布:4399元起

    全球首发天玑9500!vivo X300发布:4399元起全球首发天玑9500!vivo X300发布:4399元起全球首发天玑9500!vivo X300发布:4399元起全球首发天玑9500!vivo X300发布:4399元起

    10月13日,vivo正式推出了全新旗舰手机——vivo x300,引发广泛关注。 价格方面,该机提供多个配置版本:12GB+256GB售价为4399元,16GB+256GB定价4699元,12GB+512GB为4999元,16GB+512GB则为5299元,顶配的16GB+1TB版本售价5799元…

    2026年9月22日 用户投稿
    000
  • Canva的AI混合工具如何操作?快速设计专业图形与文本的步骤

    Canva的AI混合功能通过Magic Studio将文本、图像生成与智能设计整合,提升创作效率。首先,使用Magic Write生成文案初稿,克服空白页难题;其次,通过Magic Media输入详细描述生成定制化图像,越具体效果越好;再利用Magic Design上传图片或输入文字自动生成多种设计…

    2026年9月22日
    000
  • PHPRestfulAPI怎么开发_PHP构建高效安全的RestfulAPI教程

    答案:本文介绍如何用PHP构建高效安全的Restful API,涵盖设计规范、项目结构、数据库操作、安全机制、统一响应格式及性能优化。遵循Restful风格使用标准HTTP方法与状态码,通过index.php统一入口路由请求至控制器;采用PDO预处理防止SQL注入,结合JWT实现认证授权,确保输入验…

    2026年9月22日
    100
  • MAC的“自动操作”(Automator)怎么用_macOS自动操作创建快速工作流程

    使用Automator可创建自动化工作流程,通过选择“工作流程”并添加操作实现任务串联,保存为“快速操作”或“应用程序”便于调用,结合日历设置定时执行,并可嵌入Shell脚本扩展功能,提升Mac操作效率。 如果您希望在日常操作中提升效率,可以通过自动化重复性任务来节省时间。MAC的“自动操作”(Au…

    2026年9月22日
    000
  • MySQL服务无法启动怎么办?常见解决方法

    MySQL服务无法启动怎么办?常见解决方法MySQL服务无法启动怎么办?常见解决方法MySQL服务无法启动怎么办?常见解决方法MySQL服务无法启动怎么办?常见解决方法

    mysql服务无法启动常见原因包括配置错误、端口占用、数据文件损坏或权限问题。解决方法如下:1. 查看错误日志,定位问题根源;2. 检查配置文件是否存在语法错误或路径问题;3. 确认端口(如3306)未被占用;4. 核查数据目录的权限与完整性;5. 必要时修复或重置数据目录,甚至重新安装mysql。…

    2026年9月22日 用户投稿
    000
  • Java TreeMap如何自定义排序规则

    TreeMap默认按键的自然顺序排序,可通过构造函数传入Comparator自定义排序规则。例如字符串可按长度排序:TreeMap map = new TreeMap((s1, s2) -> s1.length() – s2.length()); 对自定义对象如Person可按年龄…

    2026年9月22日
    000
  • 如何使用MLflow训练AI大模型?模型管理与跟踪的实用教程

    如何使用MLflow训练AI大模型?模型管理与跟踪的实用教程如何使用MLflow训练AI大模型?模型管理与跟踪的实用教程如何使用MLflow训练AI大模型?模型管理与跟踪的实用教程如何使用MLflow训练AI大模型?模型管理与跟踪的实用教程

    MLflow通过实验跟踪、可复现的项目封装、标准化模型格式和集中式模型注册表,实现大模型训练的全流程管理。它记录超参数、指标和模型文件,支持分布式环境下的集中日志管理,利用远程跟踪服务器和云存储统一收集数据,并通过模型版本控制与阶段管理提升团队协作与部署效率。 ☞☞☞AI 智能聊天, 问答助手, A…

    2026年9月22日 用户投稿
    000
  • Java Collections.synchronizedList方法如何保证线程安全

    synchronizedList通过同步方法保证线程安全,使用synchronized关键字对每个操作加锁,确保单个操作的原子性;但迭代或复合操作需手动同步,否则可能引发并发异常;其性能较低,适用于读多写少、并发不高的场景,高并发下推荐使用CopyOnWriteArrayList。 Java 中 C…

    2026年9月22日
    100
  • 如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程

    如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程

    MiniTool MovieMaker虽无AI生成功能,但可高效编辑AI生成的MP4、MOV等格式视频或图片序列。通过导入素材后,利用其剪辑、过渡、滤镜、文字、音频处理等功能,实现AI片段的精剪、色彩统一、无缝衔接与风格化输出。支持主流视频、图片及音频格式,兼容性好,适合个人创作者进行AI内容后期整…

    2026年9月22日 用户投稿
    600
  • VSCode如何调试JavaScript代码 VSCode调试功能的实战技巧

    要在vscode中调试javascript,首先需设置断点、配置launch.json文件、选择合适的调试环境并启动调试会话;2. launch.json至关重要,常见陷阱包括program路径错误、type类型不匹配、cwd设置不当、混淆launch与attach模式以及source map配置缺…

    2026年9月22日
    000
  • 如何修改MySQL的默认端口号?

    如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?

    修改mysql默认端口号需编辑配置文件,核心步骤为:1.定位my.cnf或my.ini文件;2.在[mysqld]段落中修改或添加port参数;3.保存后重启mysql服务。更改端口主要出于避免冲突、提升安全性和适应网络策略考虑。连接时需在客户端工具或代码中指定新端口,如命令行加-p参数、编程语言连…

    2026年9月22日 用户投稿
    1200
  • 荣耀GT 2系列配置曝光:9000mAh电池+165Hz直屏

    荣耀GT 2系列配置曝光:9000mAh电池+165Hz直屏荣耀GT 2系列配置曝光:9000mAh电池+165Hz直屏荣耀GT 2系列配置曝光:9000mAh电池+165Hz直屏荣耀GT 2系列配置曝光:9000mAh电池+165Hz直屏

    10月13日,一位数码博主透露,某款搭载骁龙8系旗舰平台的新机正在规划一款硅基电池,其容量预计将达到约9000mah。这一超大电池容量在当前智能手机市场中极为少见,意味着该机型在续航能力上将拥有显著优势。结合该博主的暗示以及网友的讨论分析,这款新机极有可能是即将发布的荣耀gt 2系列。 荣耀GT P…

    2026年9月22日 用户投稿
    100
  • PHP匿名函数怎么用_PHP匿名函数使用场景分析

    PHP匿名函数是无名函数,可作为回调或赋值给变量,常用在数组处理、事件回调、逻辑封装等场景,支持use引入外部变量及fn短语法,结合bindTo可访问对象私有成员。 PHP匿名函数,也叫闭包函数(Closure),是一种没有名称的函数,通常作为回调使用或赋值给变量。它在实际开发中非常灵活,尤其适合用…

    2026年9月22日
    200
  • 中国联通正式获得开展 eSIM 手机运营服务商用试验的批复

    感谢网友 会弹琴的九号、学士 的线索投递! 10月13日,三大运营商官方微信号相继发布消息,宣告eSIM服务进入新阶段。其中,中国联通于当日上午10:00率先发布推文《抢约!联通eSIM来了!》,动作迅速,展现出强烈的市场积极性;中国移动在傍晚19:29发布《中国移动全面上线eSIM手机办理》;而中…

    2026年9月22日
    200
  • 为什么建议手动定义Java序列化ID

    手动定义serialVersionUID可确保序列化兼容性,避免因类结构变化导致反序列化失败。Java默认生成的ID依赖类名、字段等信息,编译环境或代码微小改动均使其改变,易引发InvalidClassException。显式声明后,可在兼容性变更时主动控制ID更新,保留原ID则允许旧版本读取新对象…

    2026年9月22日
    200

发表回复

登录后才能评论
关注微信