1. 概述
在使用Java编程时,我们有时需要根据给定的对象列表生成另一个类型不同的列表。Java 8引入了一系列新特性,简化了这类操作。
本教程将介绍如何利用Java 8及以后版本的强大功能,基于给定的员工对象列表创建一个不同类型的网球选手候选人列表。
2. 问题介绍
如常,让我们通过实例来理解这个问题。
假设一家公司要举办内部网球比赛,赛委会需要从所有员工中筛选出参赛者候选人。我们将构建一个程序来构建这个候选人名单。
Employee 类已经准备好了:
@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);
}
}
代码中使用了Lombok的注解 @Getter,使Employee 类具有所有属性的getter方法。
每个Employee对象都有一个名为hobbies的Set,用于存储员工的爱好,以字符串形式表示。我们的任务是遍历员工,如果某员工的爱好包含“网球”,则认为他们是网球选手的候选人。最终,我们将得到一个TennisPlayerCandidate 实例的列表:
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
}
作为输入,假设EMPLOYEES 列表包含五个对象:
final static List<Employee> EMPLOYEES = Lists.newArrayList(
new Employee("Kai", "[email protected]", Lists.newArrayList("Football", "Reading", "Chess")),
new Employee("Eric", "[email protected]", Lists.newArrayList("Tennis", "Baseball", "Singing")),
new Employee("Saajan", "[email protected]", Lists.newArrayList("Tennis", "Baseball", "Reading")),
new Employee("Kevin", "[email protected]", Lists.newArrayList("Dancing", "Computer Games", "Tennis")),
new Employee("Amanda", "[email protected]", Lists.newArrayList("Painting", "Yoga", "Dancing"))
);
目标是根据这个输入,得到如下TennisPlayerCandidate 列表:
final static List<TennisPlayerCandidate> EXPECTED = Lists.newArrayList(
new TennisPlayerCandidate("Eric", "[email protected]"),
new TennisPlayerCandidate("Saajan", "[email protected]"),
new TennisPlayerCandidate("Kevin", "[email protected]")
);
接下来,我们将探讨不同的方法,从给定的Employee 列表构建预期的TennisPlayerCandidate 列表。
为了简化,我们将使用单元测试断言来验证每种方法是否能产生预期结果。
3. 使用List.forEach()
方法
解决这个问题的一个直接方法是首先初始化一个空的候选人列表。然后,遍历EMPLOYEES 列表,对于列出“网球”爱好的每个员工,创建一个TennisPlayerCandidate 对象,并将其添加到候选人列表中,如果满足这个条件。
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);
正如我们所见,这种方法可以有效地完成任务。
除了forEach()
方法,自Java 8以来,Stream API彻底改变了我们处理和转换数据集合的方式。
接下来,我们将使用Stream API来解决问题。
4. 使用Stream.map()
或Collectors.mapping()
我们可以这样解读问题:过滤出爱好包含网球的员工,并将这些Employee对象转换为TennisPlayerCandidate对象。
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);
如代码所示,无需预先准备TennisPlayerCandidate对象的列表。**filter().map()
管道会返回一个TennisPlayerCandidate实例的Stream。我们需要做的只是将对象收集到一个列表中。**
另一种选择是将映射逻辑移动到收集阶段。换句话说,在收集过滤后的Employee实例时,我们将它们转换为TennisPlayerCandidate对象。
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. 总结
在这篇文章中,我们探讨了三种根据给定列表创建不同类型对象列表的方法。通过实例,我们了解到在Java中使用Stream API处理列表时,可以提高代码的生产力和可读性。
如往常一样,示例的完整源代码可在GitHub上找到。