
本文详细介绍了在java中如何将包含嵌套列表(如支付对象中包含多个交易列表)的复杂对象列表进行转换。目标是将每个原始对象根据其嵌套列表中的元素进行展平,生成一个新列表,其中每个新对象仅包含一个嵌套元素,同时保留原始对象的其他属性。文章将通过java 7、java 8-15以及java 16及更高版本提供的不同编程范式进行演示,帮助开发者高效地处理此类数据结构转换。
在软件开发中,我们经常需要处理复杂的数据结构,其中一个常见场景是对象内部包含一个嵌套的列表。例如,一个支付对象(Pmt)可能包含多个交易(Transaction)记录。然而,在某些业务逻辑或数据处理需求下,我们可能需要将这种一对多的关系“展平”为一对一的关系,即为每个嵌套的交易记录创建一个独立的支付对象副本,同时保留原始支付对象的其他属性。
场景描述
假设我们有以下两个Java类定义:
public class A {
String a;
String b;
String v;
List pmtList; // 包含支付对象列表
// ... 其他属性和方法
}
public class Pmt {
String id; // 支付ID
String b; // 其他支付属性
List trList; // 嵌套的交易列表
// ... 其他属性和方法
// 构造函数示例,用于创建新的Pmt对象
public Pmt(String id, String b, List trList) {
this.id = id;
this.b = b;
this.trList = trList;
}
// Getter方法
public String getId() { return id; }
public String getB() { return b; }
public List getTrList() { return trList; }
}
public class Transaction {
// 交易的详细信息
// ... 属性和方法
} 我们的目标是,给定一个 List
例如,如果原始列表中有 5 个 Pmt 对象,每个 Pmt 对象包含 2 个 Transaction,那么新列表将包含 10 个 Pmt 对象。
立即学习“Java免费学习笔记(深入)”;
解决方案
以下将介绍三种在不同Java版本中实现此转换的方法。为了使这些解决方案生效,Pmt 类需要一个合适的构造函数,能够接收原始 Pmt 的属性以及一个包含单个 Transaction 的列表。例如,可以添加如下构造函数:
import static java.util.Collections.singletonList; // 静态导入,方便创建单元素列表
public class Pmt {
// ... 现有属性和方法
public Pmt(String id, String b, List trList) {
this.id = id;
this.b = b;
this.trList = trList;
}
} 1. Java 7 及更早版本(或兼容性方案)
在Java 7及更早的版本中,通常使用传统的迭代循环来实现此类转换。通过嵌套的 forEach 循环,我们可以遍历每个支付对象及其内部的交易列表,并为每个交易创建一个新的支付对象。
import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.singletonList;
public class PmtTransformer {
public List transformPmtListJava7(List pmtList) {
List newList = new ArrayList<>();
for (Pmt p : pmtList) {
for (Transaction tr : p.getTrList()) {
// 为每个交易创建一个新的Pmt对象,并将其添加到新列表中
newList.add(new Pmt(p.getId(), p.getB(), singletonList(tr)));
}
}
return newList;
}
} 说明: 这种方法直观易懂,通过两层循环,外层遍历 pmtList 中的每个 Pmt 对象,内层遍历当前 Pmt 对象中的 trList。对于 trList 中的每个 Transaction,我们都创建一个新的 Pmt 对象,其中包含原始 Pmt 的 id 和 b 属性,以及一个只包含当前 Transaction 的新列表。
2. Java 8 - Java 15(Stream API 结合 flatMap)
Java 8引入的Stream API为集合操作提供了更声明式和函数式的方法。flatMap 操作特别适合处理这种一对多的转换场景。它能够将一个流中的每个元素转换成零个、一个或多个元素的流,然后将这些流连接成一个单一的流。
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Collections.singletonList;
public class PmtTransformer {
public List transformPmtListJava8(List pmtList) {
return pmtList.stream()
.flatMap(p -> p.getTrList().stream() // 将每个Pmt对象的trList转换为Transaction流
.map(tr -> new Pmt(p.getId(), p.getB(), singletonList(tr)))) // 为每个Transaction创建新的Pmt对象
.collect(Collectors.toList()); // 收集结果到新的List中
}
} 说明:
- pmtList.stream():将原始 Pmt 列表转换为一个 Pmt 对象的流。
- .flatMap(p -> ...):对于流中的每个 Pmt 对象 p:
- p.getTrList().stream():获取 p 内部的 trList,并将其转换为一个 Transaction 对象的流。
- .map(tr -> new Pmt(p.getId(), p.getB(), singletonList(tr))):对于这个 Transaction 流中的每个 tr,创建一个新的 Pmt 对象。这个新的 Pmt 对象会复制原始 p 的 id 和 b 属性,但其 trList 只包含当前的 tr。
- flatMap 会将所有这些内部生成的 Pmt 对象流扁平化为一个单一的 Pmt 对象流。
- .collect(Collectors.toList()):将最终的 Pmt 对象流收集到一个新的 List
中。
3. Java 16 及更新版本(Stream API 结合 mapMulti)
Java 16引入了 mapMulti 方法,它提供了一种更灵活、可能更高效的方式来处理一对多(或多对多)的流转换,尤其是在需要更精细控制或避免创建中间流时。它允许通过一个 Consumer 回调函数来发出零个、一个或多个元素。
import java.util.List;
import java.util.function.Consumer;
import static java.util.Collections.singletonList;
public class PmtTransformer {
public List transformPmtListJava16(List pmtList) {
return pmtList.stream()
.mapMulti((Pmt p, Consumer c) -> { // 对每个Pmt对象进行操作
p.getTrList().forEach(tr ->
c.accept(new Pmt(p.getId(), p.getB(), singletonList(tr)))); // 为每个Transaction发出一个Pmt对象
})
.toList(); // Java 16+ 提供的便捷方法,直接收集为不可变列表
}
} 说明:
- pmtList.stream():与Java 8方案相同,创建 Pmt 对象的流。
- .
mapMulti((Pmt p, Consumer c) -> { ... }): - mapMulti 接收一个 BiConsumer,其中第一个参数是当前流元素(Pmt p),第二个参数是一个 Consumer
c。 - 在这个 BiConsumer 内部,我们遍历 p.getTrList()。
- 对于每个 Transaction tr,我们调用 c.accept(new Pmt(...)) 来将新创建的 Pmt 对象“发出”到下游流中。
- mapMulti 会将所有这些发出的元素组合成一个单一的流。
- mapMulti 接收一个 BiConsumer,其中第一个参数是当前流元素(Pmt p),第二个参数是一个 Consumer
- .toList():这是Java 16引入的一个便捷方法,用于将流中的所有元素收集到一个新的不可变 List 中。
总结
本文详细介绍了在Java中将包含嵌套列表的对象进行展平转换的三种主要方法。
- Java 7 及更早版本:使用传统的嵌套 for 循环,代码直观,易于理解,但在处理大量数据时可能不如Stream API简洁。
- Java 8 - Java 15:利用Stream API的 flatMap 方法,实现了声明式的数据转换。这种方式代码简洁,可读性强,是现代Java开发中处理此类场景的推荐方法。
- Java 16 及更新版本:引入了 mapMulti,为一对多转换提供了更灵活的控制,并且在某些情况下可能提供更好的性能,因为它避免了创建中间流。同时,toList() 方法进一步简化了收集操作。
选择哪种方法取决于您的项目所使用的Java版本以及个人偏好。对于新项目,推荐使用Java 8或更高版本的Stream API,以享受其带来的简洁性和表达力。无论选择哪种方法,核心思想都是遍历嵌套列表,并为每个子元素创建原始对象的一个新副本,从而实现数据的展平。










