如何在流中应用flatMap()实现以下逻辑



我有以下for循环:

List<Player> players = new ArrayList<>();
for (Team team : teams) {
ArrayList<TeamPlayer> teamPlayers = team.getTeamPlayers();
for (teamPlayer player : teamPlayers) {
players.add(new Player(player.getName, player.getPosition());
}
}

,我正试图将其转换为流:

List<Player> players = teams.forEach(t -> t.getTeamPlayers()
.forEach(p -> players.add(new Player(p.getName(), p.getPosition())))
);

但是我得到一个编译错误:

variable 'players' might not have been initialized

为什么会发生这种情况?也许有另一种方法来创建流,我正在考虑使用flatMap,但不确定如何应用它。

首先,您需要了解Streams

因此,不要试图模仿循环。检查API提供的工具。操作forEach()用于特殊情况,当您需要执行副作用而不是时以便将流中的元素累积到Collection中。

注意:对于teams.forEach(),您实际上不是在使用流,而是方法Iterable.forEach(),该方法可用于Iterable的每个实现。

为了在流上执行缩减,我们有几个专门的操作,如collect,reduce等(更多信息参见API文档- reduction)。

collect()操作意味着执行可变约简。您可以通过提供内置CollectorCollectors.toList()作为参数来将数据收集到列表中。由于Java 16操作toList()被引入到API中,它是在toArray()操作的基础上实现的,并且比同名收集器性能更好(因此,如果您的JDK版本允许您使用它,它是首选选项)。

我正在考虑使用flatMap,但不确定如何应用它。

操作flatMap()意味着执行一对多转换。它期望有一个函数,该函数接受一个流元素并生成结果类型的流,生成的流的元素将替代初始元素。

注意:使用尽可能少的操作来编写流的通用方法(因为函数式编程给Java带来的主要优势之一就是简单)。出于这个原因,当流元素在单步中生成Collection时应用flatMap()是习惯做法,因为它比分两步执行map().flatMap()

更排序。这就是实现的样子:

List<Team> teams = List.of();
List<Player> players = teams.stream()                // Stream<Team>
.flatMap(team -> team.getTeamPlayers().stream()) // Stream<Player>
.map(player -> new Player(player.getName(), player.getPosition()))
.toList(); // for Java 16+ or collect(Collectors.toList())

这基本上是Alexander Ivanchenko的答案,但有方法参考。

final var players = teams.stream()
.map(Team::getTeamPlayers)
.flatMap(Collection::stream)
.map(p -> new Player(p.getName(), p.getPosition()))
.toList();

如果你的Player类有一个工厂方法(取决于PlayerTeamPlayer之间的关系:

public static Player fromTeamPlayer(final TeamPlayer teamPlayer) {
return new Player(teamPlayer.getName(), teamPlayer.getPosition());
}

你可以进一步简化为:

final var players = teams.stream()
.map(Team::getTeamPlayers)
.flatMap(Collection::stream)
.map(Player::fromTeamPlayer)
.toList();

最新更新