我正在尝试将一些 for-each 循环更改为 lambda forEach()
-方法以发现 lambda 表达式的可能性。以下似乎是可能的:
ArrayList<Player> playersOfTeam = new ArrayList<Player>();
for (Player player : players) {
if (player.getTeam().equals(teamName)) {
playersOfTeam.add(player);
}
}
使用 lambda forEach()
players.forEach(player->{if (player.getTeam().equals(teamName)) {playersOfTeam.add(player);}});
但下一个不起作用:
for (Player player : players) {
if (player.getName().contains(name)) {
return player;
}
}
与 lambda
players.forEach(player->{if (player.getName().contains(name)) {return player;}});
最后一行的语法有问题还是无法从 forEach()
方法返回?
return
从lambda本身返回,而不是从任何称为lambda的地方返回。提前终止流(“短路”)使用 findFirst
,如 Ian Roberts' answer 所示。
那里的 return
从 lambda 表达式返回,而不是从包含方法返回。您需要 filter
流而不是 forEach
:
players.stream().filter(player -> player.getName().contains(name))
.findFirst().orElse(null);
这里 filter
将流限制为与谓词匹配的那些项目,然后 findFirst
返回带有第一个匹配条目的 Optional
。
这看起来比 for 循环方法效率低,但实际上 findFirst()
可以短路 - 它不会生成整个过滤后的流然后从中提取一个元素,而是只过滤所需的元素为了找到第一个匹配的。如果您不一定关心从(有序)流中获取 first 匹配的播放器,而只是关心任何匹配的项目,那么您也可以使用 findAny()
而不是 findFirst()
。当涉及并行性时,这可以提高效率。
我建议您首先尝试从整体上理解 Java 8,对您而言最重要的是流、lambda 和方法引用。
您永远不应该将现有代码逐行转换为 Java 8 代码,您应该提取特征并进行转换。
我在您的第一个案例中确定的内容如下:
如果输入结构的元素与某个谓词匹配,则您希望将它们添加到输出列表中。
让我们看看我们是如何做到这一点的,我们可以通过以下方式做到这一点:
List<Player> playersOfTeam = players.stream()
.filter(player -> player.getTeam().equals(teamName))
.collect(Collectors.toList());
你在这里做的是:
把你的输入结构变成一个流(我在这里假设它是 Collection
这还包含另外两点:
针对接口的代码,因此针对 ArrayList
现在到你的第二点:
您再次希望将旧版 Java 转换为 Java 8,而无需考虑全局。 @IanRoberts已经回答了这部分,但我认为您需要按照他的建议做players.stream().filter(...)...
。
如果你想返回一个布尔值,那么你可以使用这样的东西(比过滤器快得多):
players.stream().anyMatch(player -> player.getName().contains(name));
这对我有帮助:
List<RepositoryFile> fileList = response.getRepositoryFileList();
RepositoryFile file1 = fileList.stream().filter(f -> f.getName().contains("my-file.txt")).findFirst().orElse(null);
取自 Java 8 Finding Specific Element in List with Lambda
你也可以抛出异常:
笔记:
为了便于阅读,流的每个步骤都应在新行中列出。
players.stream()
.filter(player -> player.getName().contains(name))
.findFirst()
.orElseThrow(MyCustomRuntimeException::new);
如果您的逻辑是松散的“异常驱动”,例如您的代码中有一个地方可以捕获所有异常并决定下一步做什么。仅当您可以避免在代码库中乱扔多个 try-catch
并且抛出这些异常是针对 非常 您期望它们并且可以正确处理的特殊情况时才使用异常驱动的开发。)
Optional
上使用orElse(null)
。Optional
的要点是提供一种方法来指示值的存在或不存在,而不是重载 null(这会导致 NPE)。如果您使用optional.orElse(null)
,它会收回所有与 null 相关的问题。只有当你不能修改调用者并且它真的期待一个空值时,我才会使用它。Optional<Player>
将是一种更自然的方式来适应流范式。我只是想展示如何使用 lambdas 复制现有行为。