从 java 中的 lambda forEach() 返回
Posted
技术标签:
【中文标题】从 java 中的 lambda forEach() 返回【英文标题】:Return from lambda forEach() in java 【发布时间】:2014-06-17 21:22:59 【问题描述】:我正在尝试将一些 for-each 循环更改为 lambda forEach()
-methods 以发现 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()
方法返回?
【问题讨论】:
我还不太熟悉 lambdas 的内部结构,但是当我问自己这个问题:“你会从什么返回?”时,我最初的怀疑是它不是方法。 @Gimby 是的,return
在语句中 lambda 从 lambda 本身返回,而不是从任何称为 lambda 的地方返回。提前终止流(“短路”)使用findFirst
,如Ian Roberts' answer所示。
【参考方案1】:
你也可以抛出异常:
注意:
为了可读性,流的每一步都应该在新行中列出。
players.stream()
.filter(player -> player.getName().contains(name))
.findFirst()
.orElseThrow(MyCustomRuntimeException::new);
如果您的逻辑是松散的“异常驱动”,例如您的代码中有一个地方可以捕获所有异常并决定下一步做什么。仅当您可以避免在代码库中乱扔多个 try-catch
并且抛出这些异常是针对非常您期望并且可以正确处理的特殊情况时才使用异常驱动的开发。)
【讨论】:
【参考方案2】:如果你想返回一个布尔值,那么你可以使用这样的东西(比过滤器快得多):
players.stream().anyMatch(player -> player.getName().contains(name));
【讨论】:
【参考方案3】:这对我有帮助:
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
【讨论】:
【参考方案4】:return
从 lambda 表达式返回,而不是从包含方法返回。而不是forEach
你需要filter
流:
players.stream().filter(player -> player.getName().contains(name))
.findFirst().orElse(null);
这里filter
将流限制为那些与谓词匹配的项目,然后findFirst
返回带有第一个匹配条目的Optional
。
这看起来比 for 循环方法效率低,但实际上 findFirst()
可以短路 - 它不会生成整个过滤后的流然后从中提取一个元素,而是只过滤尽可能多的元素它需要才能找到第一个匹配的。如果您不必关心从(有序)流中获取 first 匹配的播放器,而只是关心任何匹配的项目,您也可以使用 findAny()
而不是 findFirst()
。当涉及并行性时,这可以提高效率。
【讨论】:
谢谢,这就是我想要的! Java8 中似乎有很多新东西值得探索:) 合理,但我建议您不要在Optional
上使用orElse(null)
。 Optional
的主要目的是提供一种方法来指示值的存在或不存在,而不是重载 null(这会导致 NPE)。如果您使用optional.orElse(null)
,它会收回所有与空值有关的问题。只有当你不能修改调用者并且它真的期待一个空值时,我才会使用它。
@StuartMarks 实际上,将方法返回类型修改为 Optional<Player>
将是一种更自然的方式来适应流范式。我只是想展示如何使用 lambda 复制现有行为。
for ( Part part : parts ) if ( !part.isEmpty() ) return false;我想知道什么是真正更短的。并且更清晰。 java流api真正破坏了java语言和java环境。 2020 年在任何 Java 项目中工作的噩梦。【参考方案5】:
我建议您首先尝试从整体上理解 Java 8,对您而言最重要的是流、lambda 和方法引用。
您应该从不将现有代码逐行转换为 Java 8 代码,您应该提取特征并进行转换。
我在您的第一个案例中确定如下:
如果输入结构的元素与某个谓词匹配,您希望将它们添加到输出列表中。让我们看看我们是如何做到的,我们可以通过以下方式做到这一点:
List<Player> playersOfTeam = players.stream()
.filter(player -> player.getTeam().equals(teamName))
.collect(Collectors.toList());
你在这里做的是:
-
把你的输入结构变成一个流(我在这里假设它的类型是
Collection<Player>
,现在你有一个Stream<Player>
。
使用Predicate<Player>
过滤掉所有不需要的元素,如果希望保留它,则将每个玩家映射到布尔值 true。
通过Collector
收集列表中的结果元素,这里我们可以使用标准库收集器之一,即Collectors.toList()
。
这也包含了另外两点:
-
针对接口的代码,因此针对
List<E>
的代码超过ArrayList<E>
。
对new ArrayList<>()
中的类型参数使用菱形推断,毕竟您使用的是Java 8。
现在进入你的第二点:
您再次希望将一些旧版 Java 转换为 Java 8,而无需考虑全局。这部分已经@IanRoberts回答了,不过我认为你需要按照他的建议去做players.stream().filter(...)...
。
【讨论】:
以上是关于从 java 中的 lambda forEach() 返回的主要内容,如果未能解决你的问题,请参考以下文章
Java8 Lambda 表达式中的 forEach 如何提前终止?
Java Lambda表达式forEach无法跳出循环的解决思路