将具有多个属性的对象映射到另一个对象,然后收集到列表

Map an object with multiple properties to another and then collect to list

我有一个这样的对象:

@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
    private String name;
    private String lastName;
}

还有一个,我们称它为:

@Data
@AllArgsConstructor
@NoArgsConstructor
class DBPerson {
    private String name;
    private String lastName;
}

假设两个对象都有一个带有 namelastName 的构造函数以及它们各自的 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"));

我需要将 List<DBPerson> 转换为 List<Person>,所以我尝试了这种方式:

dbPersons.stream()
.map(DBPerson::getName)
.map(Person::new)
.collect(Collectors.toList());

这显然在 new 上给我一个错误,因为在 Person class

上没有带有单个 String 的构造函数

我如何映射 DBPerson 的两个属性以创建一个新的 Person,然后将所有这些都收集到 List<Person> 中?

我试过 .forEach,但我不确定它是否是最佳解决方案,所以如果有另一种使用 Stream API 的方法,我将不胜感激更好的解决方案,否则我将不得不坚持 .forEach.

我找到了其他使用 flatMap 的解决方案,但这些解决方案用于将 namelastName 连接成一个 String,这不是我想要的。

tl;博士

向第二个 class 添加一个构造函数,该构造函数采用第一个 class 类型的参数。将 Stream#map 与该构造函数的方法引用一起使用,并收集第二个 class.

的新实例
personXes.stream().map( PersonY :: new ).toList();

Lambda 函数

与其对 getter 使用方法引用,不如编写 lambda 函数来生成新对象。

为简洁起见,我们使用 Java 16+ records 功能。

package work.basil.mix;

public record PersonX( String firstName , String lastName ) { }
package work.basil.mix;

public record PersonY( String firstName , String lastName ) { }

获取一些示例数据。

List < PersonX > xList =
        List.of(
                new PersonX( "Harry" , "Potter" ) ,
                new PersonX( "Hermione" , "Granger" ) ,
                new PersonX( "Ronald" , "Weasley" )
        );

创建该列表中元素的流。对于每个 PersonX 个对象,我们需要生成一个 PersonY 个对象。对于这样的转换,请使用 Stream#mapmap 函数的结果必须与我们新集合的类型相匹配。在我们的例子中,新集合是 List< PersonY >。所以我们的转换 map 函数必须产生一个 PersonY 对象。

最后,我们通过调用 .toList 将每个新产生的 PersonY 收集到一个不可修改的列表中。在Java 16之前,必须使用更冗长的.collect( Collectors.toList() ).

List < PersonY > yList = xList.stream().map( personX -> new PersonY( personX.firstName() , personX.lastName() ) ).toList() ;

采用交替格式。

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]]

备用构造函数

另一种方法是向第二个 class 添加构造函数,将第一个 class 的对象作为参数。然后构造函数从第一个 class 的现有对象中提取必要的数据来构造第二个 class.

的对象

如果您经常以持续的方式进行这种转换,那么这种方法很有意义。

package work.basil.mix;

public record PersonY( String firstName , String lastName ) {
    public PersonY ( PersonX personX ) {
        this( personX.firstName() , personX.lastName() );
    }
}

现在我们可以简化上面看到的代码。

List < PersonY > yList = xList.stream().map( personX -> new PersonY( personX ) ).toList();

我们可以通过使用构造函数的方法引用来进一步简化:PersonY :: new。每个 personX 对象都将隐式传递给构造函数。

下一行代码与上一行代码完全相同,只是更短,而不是更好。在这两种情况下,第一个 class 的每个流对象都被传递给第二个 class.

的构造函数
List < PersonY > yList = xList.stream().map( PersonY :: new ).toList();

在java8

    Function<DBPerson, Person> toPersonMapper =
      dbPerson -> new Person(dbPerson.getName(), dbPerson.getLastName());

    List<Person> personList =
      dbPersonList
        .stream()
        .map(toPersonMapper)
        .collect(Collectors.toList());