【问题标题】:Map an object with multiple properties to another and then collect to list将具有多个属性的对象映射到另一个对象,然后收集到列表
【发布时间】:2021-08-13 22:57:12
【问题描述】:

我有一个像这样的对象:

@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&lt;DBPerson&gt; 转换为List&lt;Person&gt;,所以我尝试了这种方式:

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

这显然在 new 上给了我一个错误,因为在 Person 类上没有带有单个 String 的构造函数

如何映射DBPerson 的两个属性以创建新的Person,然后将所有这些属性收集到List&lt;Person&gt; 中?

我已尝试使用 .forEach,但我不确定这是否是最佳解决方案,因此如果有另一种使用 Stream API 的方法,我将不胜感激任何更好的解决方案,否则我将拥有坚持.forEach

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

【问题讨论】:

  • 你需要一个普通的 lambda:dbPerson -&gt; new Person(dbPerson.getName(), dbPerson.getLastName())
  • 或者你可以创建一个构造函数Person(DBPerson dbPerson)和映射Person::new
  • 您的意思是在带有常规 lambda 的 .forEach 循环内吗? @shmosel 喜欢dbPersons.forEach(dbPerson -&gt; new Person(...))?
  • 不,那在map()里面。
  • 知道了,确实做到了,如果你能把它写成答案,我很乐意接受它

标签: java java-8 java-stream


【解决方案1】:

tl;博士

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

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&lt; PersonY &gt;。所以我们的转换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]]

备用构造函数

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

如果您要经常、持续地进行这种转换,这种方法是有意义的。

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 对象都会隐式传递给构造函数。

下一行代码的工作方式与上一行完全相同,只是更短,而不是更好。在这两种情况下,第一个类的每个流对象都被传递给第二个类的构造函数。

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

【讨论】:

    【解决方案2】:

    在 Java 8 中

        Function<DBPerson, Person> toPersonMapper =
          dbPerson -> new Person(dbPerson.getName(), dbPerson.getLastName());
    
        List<Person> personList =
          dbPersonList
            .stream()
            .map(toPersonMapper)
            .collect(Collectors.toList());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-27
      • 2014-04-02
      • 1970-01-01
      • 2020-06-27
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多