从Java中的lambda forEach()返回


95

我试图将一些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()方法中返回?


我还不太熟悉lambda的内部原理,但是当我问自己一个问题:“您将从哪里回来?”时,我最初的怀疑是这不是方法。
Gimby)

1
@Gimby是的,return在语句中,lambda从lambda本身返回,而不是从所谓的lambda返回。提前终止流(“短路”)findFirst,如Ian Roberts的答案所示
斯图尔特·马克

Answers:


118

return那里,从λ表达式,而不是从包含方法返回。而不forEach需要filter流:

players.stream().filter(player -> player.getName().contains(name))
       .findFirst().orElse(null);

这里filter将流限制为与谓词匹配的那些项,findFirst然后返回Optional带有第一个匹配条目的。

这看起来不如for循环方法有效,但实际上findFirst()会短路-它不会生成整个过滤后的流,然后从中提取一个元素,而是仅过滤所需数量的元素,以便找到第一个匹配的。如果您不一定要从(有序)流中获取第一个匹配的玩家,也可以使用findAny()代替,而只需选择任何匹配的项即可。当涉及并行时,这可以提高效率。findFirst()


谢谢,这就是我想要的!Java8中似乎有很多新内容可以探索:)
samutamm

10
合理,但我建议您不要orElse(null)在上使用Optional。的要点Optional是提供一种指示值的存在或不存在的方法,而不是重载null(这会导致NPE)。如果使用optional.orElse(null)它,则将所有问题归零。仅当您无法修改调用方并且确实需要null时,才使用它。
斯图尔特·马克

1
@StuartMarks实际上,将方法的返回类型修改Optional<Player>为适合流范例的一种更自然的方法。我只是试图展示如何使用lambda复制现有行为。
伊恩·罗伯茨

对于(Part part:parts)if(!part.isEmpty())返回false; 我想知道真正短的是什么。更清晰。Java流api确实破坏了Java语言和Java环境。噩梦将在2020
。– mmm

15

我建议您首先尝试从整体上理解Java 8,最重要的是,它是流,lambda和方法引用。

你应该永远现有代码转换为Java 8代码一行一行地的基础上,你应该进行特征提取和转换那些。

我在您的第一种情况下确定的内容如下:

  • 您希望将输入结构的元素添加到输出列表(如果它们与某些谓词匹配)。

让我们看看我们如何做到这一点,我们可以通过以下方式做到这一点:

List<Player> playersOfTeam = players.stream()
    .filter(player -> player.getTeam().equals(teamName))
    .collect(Collectors.toList());

您在这里所做的是:

  1. 将您的输入结构转换为流(我在这里假设它是类型的Collection<Player>,现在您有了一个Stream<Player>
  2. 使用过滤掉所有不需要的元素,Predicate<Player>如果希望保留,则将每个玩家映射到布尔值true。
  3. 通过收集结果元素到列表中,Collector这里我们可以使用标准库收集器之一Collectors.toList()

这还包含其他两点:

  1. 针对接口进行编码,因此针对List<E>over进行编码ArrayList<E>
  2. 使用中的菱形推断作为type参数new ArrayList<>(),毕竟您使用的是Java 8。

现在到第二点:

您再次想要将旧版Java转换为Java 8,而不用看大图。@IanRoberts已回答了这一部分,尽管我认为您需要按照players.stream().filter(...)...他的建议进行操作。


5

如果要返回布尔值,则可以使用如下所示的方法(比filter快得多):

players.stream().anyMatch(player -> player.getName().contains(name));


1

您还可以引发异常:

注意:

为了便于阅读,流的每一步都应在新行中列出。

players.stream()
       .filter(player -> player.getName().contains(name))
       .findFirst()
       .orElseThrow(MyCustomRuntimeException::new);

如果您的逻辑松散地由“异常驱动”,例如您的代码中有一个地方捕获所有异常并决定下一步要做什么。仅在可以避免用多个代码乱扔代码库try-catch并抛出这些异常的情况下,才使用异常驱动的开发,这些异常情况是您希望它们可以处理的非常特殊的情况。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.