将具有多个属性的对象映射到另一个对象,然后收集到列表
我有一个这样的对象:
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private String lastName;
}
还有一个,我们称之为:
@Data
@AllArgsConstructor
@NoArgsConstructor
class DBPerson {
private String name;
private String lastName;
}
假设两个对象都有一个带有name和的构造函数lastName以及它们各自的 getter 和 setter,我正在使用lombok
List<DBPerson> dbPersons = new ArrayList();
dbPersons.add(new DBPerson("Harry", "Potter"));
dbPersons.add(new DBPerson("Hermione", "Granger"));
dbPersons.add(new DBPerson("Ronald", "Weasley"));
我需要从 a 转换List<DBPerson>为List<Person>,所以我尝试了这种方式:
dbPersons.stream()
.map(DBPerson::getName)
.map(Person::new)
.collect(Collectors.toList());
这显然给了我一个错误,new因为没有带有单个StringonPerson类的构造函数
我怎么能映射两个属性DBPerson来创建一个新的Person,然后将所有这些都收集到一个List<Person>?
我已经尝试过.forEach,但是我不确定它是否是最佳解决方案,所以如果有另一种使用 Stream API 的方法,我会很感激任何更好的解决方案,否则我将不得不坚持使用.forEach.
我找到了其他解决方案,flatMap但这些解决方案用于将name和连接lastName成一个String,这不是我想要的。
回答
tl;博士
向第二个类添加一个构造函数,该构造函数接受第一个类类型的参数。Stream#map与该构造函数的方法引用一起使用,并收集第二个类的新实例。
personXes.stream().map( PersonY :: new ).toList();
personXes.stream().map( PersonY :: new ).toList();
Lambda 函数
与其为 getter 使用方法引用,不如编写一个 lambda 函数来生成一个新对象。
为简洁起见,让我们使用 Java 16+记录功能。
package work.basil.mix;
public record PersonX( String firstName , String lastName ) { }
获取一些示例数据。
package work.basil.mix;
public record PersonY( String firstName , String lastName ) { }
制作该列表中元素的流。对于这些PersonX对象中的每一个,我们都需要生成一个PersonY对象。对于此类转换,请使用Stream#map. 我们map函数的结果必须与我们新集合的类型相匹配。在我们的例子中,新集合是一个List< PersonY >. 所以我们的转换map函数必须产生一个PersonY对象。
最后,我们PersonY通过调用将每个新生成的内容收集到一个不可修改的列表中.toList。在 Java 16 之前,您必须使用更详细的.collect( Collectors.toList() ).
List < PersonX > xList =
List.of(
new PersonX( "Harry" , "Potter" ) ,
new PersonX( "Hermione" , "Granger" ) ,
new PersonX( "Ronald" , "Weasley" )
);
在替代格式。
List < PersonY > yList = xList.stream().map( personX -> new PersonY( personX.firstName() , personX.lastName() ) ).toList() ;
跑的时候。
yList = [PersonY[firstName=Harry,lastName=Potter],PersonY[firstName=Hermione,lastName=Granger],PersonY[firstName=Ronald,lastName=Weasley]]
替代构造函数
另一种方法是向您的第二个类添加一个构造函数,将第一个类的对象作为参数。构造函数然后从第一个类的现有对象中提取必要的数据来构造第二个类的对象。
如果您将经常以持续的方式进行这种转换,则这种方法是有意义的。
List < PersonY > yList =
xList
.stream()
.map(
personX -> new PersonY( personX.firstName() , personX.lastName() )
)
.toList();
现在我们可以简化上面看到的代码。
List < PersonY > yList = xList.stream().map( personX -> new PersonY( personX ) ).toList();
我们可以通过使用构造方法参考进一步简化:PersonY :: new。每个personX对象都将隐式传递给构造函数。
下一行代码的工作方式与上一行完全相同,只是更短,而不是更好。在这两种情况下,第一个类的每个流对象都被传递给第二个类的构造函数。
List < PersonY > yList = xList.stream().map( PersonY :: new ).toList();