无需Guava:如何用MapStruct实现List转List时填充元素索引?
需求场景
我有Source和Target两个类,其中Target类多了一个index字段。现在需要把Source对象列表转换成Target对象列表,要求Target的index字段对应原列表中Source对象的位置索引。
目前我用Guava实现了一个可行方案:
List<Target> mapList(List<Source> sources) { return Streams.mapWithIndex(sources.stream(), (source, index) -> mapElement(source, (int) index)).toList(); } abstract Target mapElement(Source source, int index);
现在想移除Guava依赖,改用MapStruct实现,但查官方文档没找到相关说明,求实现技巧。
实现方案
MapStruct本身没有内置的列表索引传递机制,但可以通过自定义默认方法结合手动遍历/原生Stream来实现需求,以下是两种可行技巧:
方法1:自定义列表映射方法,手动遍历传索引
先定义单个元素的映射接口(包含索引参数),再写一个默认的列表转换方法,手动遍历原列表并传递每个元素的索引:
@Mapper public interface SourceTargetMapper { // 单个元素映射,接收index参数,MapStruct会自动实现字段映射逻辑 Target sourceToTarget(Source source, int index); // 默认实现列表转换逻辑 default List<Target> sourcesToTargets(List<Source> sources) { List<Target> targets = new ArrayList<>(sources.size()); for (int i = 0; i < sources.size(); i++) { targets.add(sourceToTarget(sources.get(i), i)); } return targets; } }
如果需要更灵活的对象初始化,也可以用@MappingTarget方式:
@Mapper public interface SourceTargetMapper { void sourceToTarget(Source source, @MappingTarget Target target, int index); default List<Target> sourcesToTargets(List<Source> sources) { List<Target> targets = new ArrayList<>(sources.size()); for (int i = 0; i < sources.size(); i++) { Target target = new Target(); sourceToTarget(sources.get(i), target, i); targets.add(target); } return targets; } }
方法2:结合Java原生Stream实现
和你原来的Guava思路类似,但改用Java原生IntStream来遍历索引,完全摆脱Guava依赖:
@Mapper public interface SourceTargetMapper { Target sourceToTarget(Source source, int index); default List<Target> sourcesToTargets(List<Source> sources) { return IntStream.range(0, sources.size()) .mapToObj(i -> sourceToTarget(sources.get(i), i)) .collect(Collectors.toList()); } }
注意事项
- 确保
Target类的index字段有公开的setter方法,或是公共成员变量,MapStruct才能完成赋值。 - 如果
Source和Target的字段名不一致,需要添加@Mapping(source = "原字段名", target = "目标字段名")注解指定映射关系。
内容的提问来源于stack exchange,提问作者Georg Braun
相关产品推荐
相关产品推荐

