0

0

Java中高效关联父子列表的策略与实践

碧海醫心

碧海醫心

发布时间:2025-09-15 20:59:01

|

181人浏览过

|

来源于php中文网

原创

Java中高效关联父子列表的策略与实践

本文探讨了在Java中将子列表高效地关联到父列表的最佳实践。针对传统循环嵌套流过滤的低效方法,我们引入了基于哈希映射(HashMap)的优化策略。通过将子列表项预处理并存储在以父ID为键的映射中,可以显著降低时间复杂度,从O(nm)优化到O(n+m),从而提高数据处理性能,尤其适用于大规模数据集。

问题描述

在实际的软件开发中,我们经常遇到需要将具有一对多关系的子实体集合关联到其对应的父实体上的场景。例如,一个product(产品)可能包含多个productsub(产品子项)。当我们从不同的服务或数据源获取到独立的product列表和productsub列表时,需要将每个productsub正确地分配给其所属的product。

为了更好地理解,我们定义以下两个实体类:

public class Product {
    private long productId;
    private String productName;
    private BigDecimal costAmount;
    private List productSublist; // 用于存储子项
    // 构造函数、Getter和Setter方法省略
}

public class ProductSub {
    private long productId; // 指向父Product的ID
    private long productSubId;
    private String lineItemName;
    private BigDecimal subCost;
    // 构造函数、Getter和Setter方法省略
}

初始实现(低效)

一种直观的实现方式是遍历Product列表,并在每次迭代中,从ProductSub总列表中过滤出与当前Product匹配的子项。

List productList = ...; // 从服务获取的产品列表
List productSubList = ...; // 从服务获取的产品子项列表

for (Product productItem : productList) {
    // 为每个产品过滤并收集其子项
    List productSubItems = productSubList.stream()
       .filter(x -> x.getProductId() == productItem.getProductId())
       .collect(Collectors.toList());
    productItem.setProductSubList(productSubItems);
}

这种方法虽然能够正确实现功能,但其效率较低。对于productList中的每个productItem,我们都需要遍历(或流式处理)整个productSubList来查找匹配项。如果productList有n个元素,productSubList有m个元素,那么这种方法的*时间复杂度为O(n m)**。当n和m都很大时,性能瓶颈会非常明显。

优化策略:使用哈希映射

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

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

优化后的实现

以下是使用HashMap进行优化的实现示例:

Kubit.ai
Kubit.ai

一个AI驱动的产品分析平台,为产品和数据团队构建

下载
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional; // 用于处理map.get()可能返回null的情况

public class ProductAssociator {

    public void associateProductSubs(List productList, List productSubList) {
        // 1. 构建ProductId到ProductSub列表的映射
        Map> productSubMap = new HashMap<>();

        for (ProductSub productSub : productSubList) {
            // 使用computeIfAbsent确保如果键不存在,则创建一个新的ArrayList
            productSubMap.computeIfAbsent(productSub.getProductId(), k -> new ArrayList<>()).add(productSub);
        }

        // 2. 遍历Product列表,从映射中获取对应的子项
        for (Product product : productList) {
            // 获取当前产品的子项列表,如果不存在则设置为空列表
            List subs = productSubMap.get(product.getProductId());
            product.setProductSubList(Optional.ofNullable(subs).orElse(new ArrayList<>()));
        }
    }

    // 示例用法
    public static void main(String[] args) {
        // 模拟数据
        List products = new ArrayList<>();
        products.add(new Product(1L, "Laptop", BigDecimal.valueOf(1200)));
        products.add(new Product(2L, "Mouse", BigDecimal.valueOf(25)));
        products.add(new Product(3L, "Keyboard", BigDecimal.valueOf(75)));

        List productSubs = new ArrayList<>();
        productSubs.add(new ProductSub(1L, 101L, "CPU", BigDecimal.valueOf(500)));
        productSubs.add(new ProductSub(1L, 102L, "RAM", BigDecimal.valueOf(200)));
        productSubs.add(new ProductSub(2L, 201L, "Wireless", BigDecimal.valueOf(15)));
        productSubs.add(new ProductSub(1L, 103L, "SSD", BigDecimal.valueOf(150)));

        ProductAssociator associator = new ProductAssociator();
        associator.associateProductSubs(products, productSubs);

        // 打印结果以验证
        for (Product p : products) {
            System.out.println("Product: " + p.getProductName() + " (ID: " + p.getProductId() + ")");
            if (p.getProductSublist() != null && !p.getProductSublist().isEmpty()) {
                for (ProductSub ps : p.getProductSublist()) {
                    System.out.println("  - SubItem: " + ps.getLineItemName() + " (SubID: " + ps.getProductSubId() + ")");
                }
            } else {
                System.out.println("  - No sub-items.");
            }
        }
    }
}

代码解析:

  1. 构建映射:
    • 我们创建了一个HashMap>,其中键是productId,值是该产品对应的ProductSub列表。
    • 遍历productSubList一次。
    • map.computeIfAbsent(productSub.getProductId(), k -> new ArrayList()):这是一个非常高效的方法。如果productSub.getProductId()作为键在map中不存在,它会创建一个新的ArrayList并将其与该键关联;如果键已经存在,它会返回与该键关联的现有列表。然后,我们将当前的productSub添加到这个列表中。
  2. 关联子项:
    • 遍历productList一次。
    • 对于每个Product,使用productSubMap.get(product.getProductId())直接获取其对应的ProductSub列表。
    • Optional.ofNullable(subs).orElse(new ArrayList()):这是一个健壮的处理方式,确保即使某个Product没有对应的ProductSub,其productSublist字段也不会被设置为null,而是被设置为一个空的ArrayList,避免潜在的NullPointerException。

性能分析与注意事项

这种优化后的方法的时间复杂度为O(n + m),其中n是productList的大小,m是productSubList的大小。这是因为我们只遍历了productSubList一次来构建映射,然后遍历了productList一次来关联子项。与O(n * m)相比,当n和m较大时,性能提升是巨大的。

注意事项:

  1. 内存消耗: 使用HashMap会占用额外的内存来存储映射。对于非常大的productSubList,需要考虑内存限制。然而,通常情况下,这种内存开销是可接受的,并且其带来的性能收益远大于开销。
  2. 键的唯一性与散列: productId作为HashMap的键,其类型Long已正确实现了hashCode()和equals()方法,保证了哈希映射的正确性。
  3. 空列表处理: 确保当某个父产品没有子项时,其productSublist被设置为一个空列表而不是null,这有助于避免后续代码中的NullPointerException。Optional.ofNullable().orElse()模式是一个很好的实践。
  4. 数据源一致性: 这种方法假设productSubList中包含所有可能需要关联的子项。如果数据源是数据库,并且数据量极其庞大,考虑在数据库层面使用JOIN操作来预先关联数据,减少应用层的数据传输和处理负担。

总结

在Java中处理父子列表关联时,采用哈希映射(HashMap)进行预处理是一种高效且推荐的策略。它将时间复杂度从O(n * m)显著优化到O(n + m),极大地提升了处理大规模数据集时的性能。通过构建一个以父ID为键的子项列表映射,我们可以实现O(1)的平均时间查找,从而避免了重复的全局遍历,使得代码更加高效、专业。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

63

2026.01.14

热门下载

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

精品课程

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

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46.1万人学习

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

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