将DTO对象列表转换为嵌套有序映射及有序列表
DTO转嵌套映射的排序与顺序保留问题
需求说明
需要将一组DTO转换为深度嵌套的映射结构:
源DTO格式
[ Dto(validationType=BASIC, source=BASE, address=A4, error=Client is a required field.), Dto(validationType=BASIC, source=BASE, address=A4, error=Client must be one of 'I', 'G' or 'W'.), Dto(validationType=BASIC, source=BASE, address=B5, error=Individuals require First and Last Names.), Dto(validationType=BASIC, source=BASE, address=D6, error=Individuals require First and Last Names.), Dto(validationType=BASIC, source=BASE, address=J7, error=First is a required field.), Dto(validationType=BASIC, source=BASE, address=L8, error=Last is a required field.), Dto(validationType=BASIC, source=BASE, address=M9, error=Name is a required field.), Dto(validationType=BASIC, source=BASE, address=N10, error=Status is a required field.), Dto(validationType=BASIC, source=BASE, address=N10, error=Status must be one of 'A' or 'P'.), Dto(validationType=BASIC, source=BASE, address=F16, error=Groups require a Group Name.) ]
目标映射格式
{ BASIC = { BASE = { A4 = [ "Client is a required field.", "Client must be one of 'I', 'G' or 'W'." ], B5 = [ "Individuals require First and Last Names." ], D6 = [ "Individuals require First and Last Names." ], J7 = [ "First is a required field." ], L8 = [ "Last is a required field." ], M9 = [ "Name is a required field." ], N10 = [ "Status is a required field.", "Status must be one of 'A' or 'P'." ], F16 = [ "Groups require a Group Name." ] } } }
现有代码问题
当前使用的转换代码存在两个问题:
final Map<String, Map<String, Map<String, ArrayList<String>>>> collect = dtoList.stream() .collect( Collectors.groupingBy( Dto::getValidationType, Collectors.groupingBy( Dto::getSource, Collectors.groupingBy( Dto::getAddress, Collectors.mapping( Dto::getError, Collectors.collectingAndThen( Collectors.toSet(), ArrayList::new))))));
- 地址键未排序:BASE下的A4、B5等键是无序的,默认
groupingBy使用HashMap,不保证顺序。 - 错误列表顺序丢失:使用
Collectors.toSet()会打乱原始DTO中的错误顺序,因为Set不保留元素顺序。
解决方案
关键修改点
- 用
TreeMap替代HashMap:TreeMap会按照键的自然顺序排序,满足地址键排序需求。 - 直接收集到
ArrayList:跳过Set,直接将错误信息按原始顺序收集到列表中。
修改后的代码
final Map<String, Map<String, Map<String, List<String>>>> collect = dtoList.stream() .collect( // 第一层:按validationType分组,用TreeMap排序 Collectors.groupingBy( Dto::getValidationType, TreeMap::new, // 第二层:按source分组,用TreeMap排序 Collectors.groupingBy( Dto::getSource, TreeMap::new, // 第三层:按address分组,用TreeMap排序 Collectors.groupingBy( Dto::getAddress, TreeMap::new, // 直接收集错误到ArrayList,保留原始顺序 Collectors.mapping( Dto::getError, Collectors.toCollection(ArrayList::new)) ) ) ) );
代码说明
- 排序处理:每一层
groupingBy都指定TreeMap::new作为Map的实现,这样validationType、source、address的键都会按照自然字符串顺序排列。 - 顺序保留:内层使用
Collectors.toCollection(ArrayList::new)直接将错误信息按DTO的原始顺序收集到列表中,不再经过无序的Set,完全保留源数据的顺序。
内容的提问来源于stack exchange,提问作者arnaldop
相关产品推荐
相关产品推荐

