
在业务开发中,我们经常会遇到需要将关联的子项数据集合添加到主对象列表中的场景。例如,我们有一个产品(product)列表,以及一个产品子项(productsub)列表。每个产品子项都通过 productid 关联到特定的产品。我们的目标是将所有属于某个产品的子项收集起来,并设置到该产品的 productsublist 属性中。
以下是相关的类定义:
public class Product {
private long productId;
private String productName;
private BigDecimal costAmount;
private List<ProductSub> 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<ProductSub> getProductSublist() { return productSublist; }
public void setProductSublist(List<ProductSub> 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<Product> productList = /* 从服务获取 Product 列表 */;
List<ProductSub> productSubList = /* 从服务获取 ProductSub 列表 */;
// 初始实现方式
for (Product productItem : productList){
List<ProductSub> 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 列表即可。
这种方法的步骤如下:
以下是优化后的代码示例:
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<Product> productList, List<ProductSub> productSubList) {
// 步骤1 & 2: 预处理 ProductSub 列表,将其按 productId 分组到 Map 中
Map<Long, List<ProductSub>> 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<ProductSub> associatedSubList = productSubMap.getOrDefault(product.getProductId(), new ArrayList<>());
product.setProductSubList(associatedSubList);
}
}
// 示例用法
public static void main(String[] args) {
// 模拟数据
List<Product> 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<ProductSub> 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()));
});
}
}优化后的方法,其时间复杂度显著降低。
因此,整体的时间复杂度为 O(n + m)。与初始的 O(n * m) 相比,这是一个巨大的改进,尤其是在 n 和 m 值较大的情况下。例如,如果 n=1000, m=1000,初始方法需要大约 1,000,000 次操作,而优化方法只需要大约 2,000 次操作。
Map<Long, List<ProductSub>> productSubMap = productSubList.stream()
.collect(Collectors.groupingBy(ProductSub::getProductId));这种方式同样能达到 O(m) 的时间复杂度,并且代码更具声明性。
在Java中处理父子列表关联的场景时,选择合适的算法和数据结构至关重要。直接的嵌套循环或在循环内部进行过滤操作,虽然直观,但其 O(n * m) 的时间复杂度在数据量较大时会成为性能瓶颈。通过引入哈希映射进行预处理,将子列表按关联ID分组,我们可以将时间复杂度优化到 O(n + m),从而实现更高效、更具扩展性的数据关联操作。这种模式不仅适用于产品与子项的场景,也广泛适用于其他一对多关系的数据集合关联。
以上就是Java中高效关联父子列表数据的优化策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号