【问题标题】:Is it discouraged to use if in java streams?是否不鼓励在 java 流中使用 if?
【发布时间】:2021-11-08 22:09:19
【问题描述】:

考虑以下问题:我应该关掉房间里的所有灯。该房间包含在一组房间中。是否不鼓励使用 forEach 和 if? (我读了我所有的讲义,他们没有提到 .forEach(p -> {if... 构造.. 但是我发现它是最简单的。如果不鼓励我如何解决问题?为什么不鼓励?

public void turnOffLampsInRooms(Set<Room> rooms) {
    lamps.stream()
    .forEach(p -> {if (p.getRoom() != null && rooms.contains(p.getRoom())) {
        p.turnOff();
    }   
    });
}

【问题讨论】:

  • 对于给定的情况:是的。我们可以在调用forEach之前两个filters
  • 正如@Turing85 指出的那样:这就是.filter(...) 的用途。
  • 请edit发帖并分享您的尝试。
  • 使用普通循环即可。更清晰、更短、更高效。
  • 在这里创建一个新的HashSet (new HashSet&lt;&gt;(lamps)) 有什么意义?为什么不直接使用lamps.stream()?

标签: java java-stream


【解决方案1】:

事实是,您使用优化良好的 Stream API,if 条件稍慢。它的语法是正确的,可以正常工作,但它看起来不太好,并且对于大量数据来说速度较慢。

首先您应该将if 语句移动到.filter 流调用:

public void turnOffLampsInRooms(Set<Room> rooms) {
    Set<SmartLamp> set = new HashSet<>(lamps);
    set.stream()
            .filter(p -> p.getRoom() != null && rooms.contains(p.getRoom()))
            .forEach(p -> p.turnOff()); // conditions checked already
}

接下来,.filter 可以拆分成独立的:

public void turnOffLampsInRooms(Set<Room> rooms) {
    Set<SmartLamp> set = new HashSet<>(lamps);
    set.stream()
            .filter(p -> p.getRoom() != null)
            .filter(p -> rooms.contains(p.getRoom())) // the same meaning
            .forEach(p -> p.turnOff());
}

最后更改对方法引用的调用:

public void turnOffLampsInRooms(Set<Room> rooms) {
    Set<SmartLamp> set = new HashSet<>(lamps);
    set.stream()
            .filter(p -> p.getRoom() != null)
            .filter(p -> rooms.contains(p.getRoom()))
            .forEach(SmartLamp::turnOff); // method reference
}

主要优点是流优化,但也更容易逐步阅读流的作用。

【讨论】:

  • 我现在明白了..我已经习惯了 if 语句我很难使用流...
  • 我意识到if 会比使用流慢。条件相同,两种情况都必须执行。并且流还必须以某种方式具有一些分支,具体取决于该条件。对此用例的 JMH 测试导致 if 的速度提高了约 10%。
  • 流不会变魔术。不要以“性能”为由使用filter 而不是if。
  • @user16320675 最主要的原因,Stream API 不与循环竞争这样的例子,是 HotSpot 的默认设置 -XX:MaxInlineLevel=9。我们在日常工作中使用 -XX:MaxInlineLevel=20 设置覆盖它,并且不会明显增加 JIT 编译时间,但流性能要好得多。当然,它并不比手动编写的 if 快,因为它仍然在后台执行等效的 if。
  • @user16320675 我知道你不是在谈论循环,但循环是可能比较的极端,循环 vs forEach() vs stream().forEach() vs stream( ).filter().forEach()。这些方法在达到相同性能所需的内联数量上有所不同,因为它们在基本层面上仍然在做同样的事情。这就是我想说的,优化器可能会在适当的情况下消除所有抽象,但是手动 if 不会比隐藏在流管道阶段的 if 慢。
【解决方案2】:

不使用if,而是使用条件过滤:

set.stream()
.filter(p -> p.getRoom() != null)
.filter(p -> rooms.contains(p.getRoom()))
.forEach(SmartLamp::turnOff);

注意方法引用SmartLamp::turnOff的使用,它通常优于等效的lambda p -&gt; p.turnOff()。

【讨论】:

    猜你喜欢
    • 2014-05-15
    • 1970-01-01
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-22
    • 1970-01-01
    相关资源
    最近更新 更多