Create List of Object From Another Type Using Java 8 – 使用 Java 8 从另一种类型创建对象列表

最后修改: 2023年 9月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述</span

When we work with Java, sometimes we want to generate a list from another list of objects. Java 8 introduced a set of new features that streamline such operations.

当我们使用 Java 时,有时会希望从另一个对象列表生成一个列表。Java 8 引入了一系列简化此类操作的新功能。

So, in this tutorial, we’ll explore how to create a list of objects of a different type based on a given list, using the powerful features introduced in Java 8 and beyond.

因此,在本教程中,我们将探讨如何使用 Java 8 及其他版本中引入的强大功能,根据给定列表创建不同类型的对象列表。

2. Introduction to the Problem

2.问题介绍

As usual, let’s understand the problem through examples.

像往常一样,让我们通过实例来理解这个问题。

Let’s say a company is looking to kickstart an internal tennis tournament. Now, the tournament committee wants a player candidate list from all company employees. So, we’ll take this task and create a program to build up the player candidate list.

比方说,一家公司希望启动一项内部网球赛。现在,赛事委员会需要一份来自公司所有员工的球员候选名单。因此,我们要完成这项任务,并创建一个程序来建立球员候选名单。

The Employee class is ready:

雇员类已准备就绪:

@Getter
class Employee {
    private final String name;
    private final Set<String> hobbies = new HashSet<>();
    private final String email;
    private String department;
    // ... other attributes

    public Employee(String name, String email, Collection<String> hobbies) {
        this.name = name;
        this.email = email;
        this.hobbies.addAll(hobbies);
    }
}

As the code above shows, we use Lombok‘s @Getter annotation to make the Employee class have getter methods for all properties.

如上面的代码所示,我们使用 Lombok@Getter 注解来使 Employee 类拥有所有属性的 getter 方法。

Each Employee object carries the hobbies Set, which holds the employee’s hobbies in String. Our task involves walking through the employees. If an employee lists “Tennis” as one of their hobbies, we consider them a potential candidate to participate as a tennis player in the tournament. Thus, in the end, we’ll have a list of TennisPlayerCandidate instances:

每个 Employee 对象都带有 hobbies Set String,其中以 String 保存了员工的爱好。如果某个员工将”网球“列为爱好之一,我们就将其视为参加比赛的网球运动员的潜在候选者:

class TennisPlayerCandidate {
    private final String name;
    private final String email;
    private final Boolean confirmed = Boolean.FALSE;
    public TennisPlayerCandidate(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
  // equals() and hashcode() methods are omitted
}

As our input, let’s assume the EMPLOYEES list contains five objects:

作为我们的输入,假设 EMPLOYEES 列表包含五个对象:

final static List<Employee> EMPLOYEES = Lists.newArrayList(
  new Employee("Kai", "kai@company.com", Lists.newArrayList("Football", "Reading", "Chess")),
  new Employee("Eric", "eric@company.com", Lists.newArrayList("Tennis", "Baseball", "Singing")),
  new Employee("Saajan", "saajan@company.com", Lists.newArrayList("Tennis", "Baseball", "Reading")),
  new Employee("Kevin", "kevin@company.com", Lists.newArrayList("Dancing", "Computer Games", "Tennis")),
  new Employee("Amanda", "amanda@company.com", Lists.newArrayList("Painting", "Yoga", "Dancing"))
);

Based on this input, we aim to get this list of TennisPlayerCandidate instances:

根据此输入,我们将得到 TennisPlayerCandidate 实例的列表:

final static List<TennisPlayerCandidate> EXPECTED = Lists.newArrayList(
  new TennisPlayerCandidate("Eric", "eric@company.com"),
  new TennisPlayerCandidate("Saajan", "saajan@company.com"),
  new TennisPlayerCandidate("Kevin", "kevin@company.com")
);

Next, let’s see different solutions to build up the expected List<TennisPlayerCandidate> from the given List<Employee>.

接下来,让我们看看从给定的 List<Employee> 中建立预期 List<TennisPlayerCandidate> 的不同解决方案。

For simplicity, we’ll use unit test assertions to verify whether each approach can produce the expected result.

为简单起见,我们将使用单元测试断言来验证每种方法是否能产生预期结果。

3. Using the List.forEach() Method

3.使用 List.forEach() 方法

One straightforward approach to solve this problem is to start by initializing an empty candidate list. Then, we traverse the EMPLOYEES list, creating a TennisPlayerCandidate object for each employee who lists tennis as a hobby. We add employees to the candidate list if they meet this criterion.

解决这一问题的一种直接方法是首先初始化一个空的候选人列表。然后,我们遍历 EMPLOYEES 列表,为每个将网球列为爱好的员工创建一个 TennisPlayerCandidate 对象。如果员工符合这一标准,我们就将其添加到候选列表中。

Java 8 introduced the forEach() method, which allows us to perform actions while traversing through a list conveniently:

Java 8 引入了forEach()方法,该方法允许我们在遍历列表时方便地执行操作:

List<TennisPlayerCandidate> result = new ArrayList<>();
EMPLOYEES.forEach(e -> {
    if (e.getHobbies().contains("Tennis")) {
        result.add(new TennisPlayerCandidate(e.getName(), e.getEmail()));
    }
});
assertEquals(EXPECTED, result);

As we can see, this approach does the job effectively.

我们可以看到,这种方法能有效地完成任务。

Apart from the forEach() method, since Java 8, the Stream API has revolutionized how we manipulate and transform data collections.

除了 forEach() 方法之外,自 Java 8 以来,Stream API 彻底改变了我们操作和转换数据集合的方式。

Next, let’s use the Stream API to solve the problem.

接下来,让我们使用 Stream API 来解决问题。

4. Using Stream.map() or Collectors.mapping()

4.使用 Stream.map()Collectors.mapping()

We can interpret the problem in this way: filtering the employees whose hobbies cover tennis and transforming these Employee objects into TennisPlayerCandidate objects.

我们可以这样理解这个问题:过滤爱好包括网球的员工,并将这些 Employee 对象转换为 TennisPlayerCandidate 对象。

Stream’s filter() and map() methods can support us to finish the task easily. Next, let’s “translate” the idea into Java code:

Stream 的 filter()map() 方法可以帮助我们轻松完成任务。接下来,让我们将这一想法 “翻译 “成 Java 代码:

List<TennisPlayerCandidate> result = EMPLOYEES.stream()
  .filter(e -> e.getHobbies().contains("Tennis"))
  .map(e -> new TennisPlayerCandidate(e.getName(), e.getEmail()))
  .collect(Collectors.toList());
assertEquals(EXPECTED, result);

As the code above shows, preparing an empty list for TennisPlayerCandidate objects is unnecessary. The filter().map() pipeline delivers a Stream of TennisPlayerCandidate instances. What we need to do is just collect the objects into a list.

正如上面的代码所示,为 TennisPlayerCandidate 对象准备一个空列表是不必要的。filter().map()管道提供了一个由TennisPlayerCandidate实例组成的。我们需要做的只是将对象收集到一个列表中。

Alternatively, we can move the mapping logic to the collecting phase. In other words, we transform the filtered Employee instances to TennisPlayerCandidate when we collect them. 

或者,我们可以将映射逻辑转移到收集阶段。换句话说,在收集时,我们将过滤后的 Employee 实例转换为 TennisPlayerCandidate 实例。

The Collectors.mapping() method allows us to perform object transformation and collection from a Stream:

Collectors.mapping()方法允许我们从Stream中执行对象转换和收集:

List<TennisPlayerCandidate> result = EMPLOYEES.stream()
  .filter(e -> e.getHobbies().contains("Tennis"))
  .collect(Collectors.mapping(e -> new TennisPlayerCandidate(e.getName(), e.getEmail()), Collectors.toList()));
assertEquals(EXPECTED, result);

5. Conclusion

5.结论</span

In this article, we’ve explored three approaches to creating a list of objects of a different type based on a given list. Through the examples, we learned that the Stream API enhances the productivity and readability of code when working with lists in Java.

在本文中,我们探讨了基于给定列表创建不同类型对象列表的三种方法。通过这些示例,我们了解到在 Java 中处理列表时,流 API 可提高代码的生产率和可读性。

As always, the complete source code for the examples is available over on GitHub.

一如既往,示例的完整源代码可在 GitHub 上获取 .