如何避免嵌套forEach调用?

Han*_*els 9 java collections foreach java-8 java-stream

我有以下代码:

interface Device {
    // ...
    boolean isDisconnected();
    void reconnect();
}

interface Gateway {
    // ...
    List<Device> getDevices();
}

...

for (Gateway gateway : gateways) {
    for(Device device : gateway.getDevices()){
        if(device.isDisconnected()){
            device.reconnect();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想使用Stream API重构代码.我的第一次尝试如下:

gateways
    .stream()
    .forEach(
        gateway -> {
            gateway
                .getDevices()
                .parallelStream()
                .filter(device -> device.isDisconnected())
                .forEach(device -> device.reconnect())
            ;
        }
    )
;
Run Code Online (Sandbox Code Playgroud)

我不喜欢它,经过一些修改后我最终得到了这段代码:

gateways
    .parallelStream()
    .map(gateway -> gateway.getDevices().parallelStream())
    .map(stream -> stream.filter(device -> device.isDisconnected()))
    .forEach(stream -> stream.forEach(device -> device.reconnect()))
;
Run Code Online (Sandbox Code Playgroud)

我的问题是是否有办法避免嵌套forEach.

ETO*_*ETO 16

您应该使用以下代码flatMap来展平流的流map:

gateways
    .parallelStream()
    .flatMap(gateway -> gateway.getDevices().parallelStream())
    .filter(device -> device.isDisconnected())
    .forEach(device -> device.reconnect());
Run Code Online (Sandbox Code Playgroud)

我会通过使用方法引用而不是lambda表达式进一步改进它:

gateways
    .parallelStream()
    .map(Gateway::getDevices)
    .flatMap(List::stream)
    .filter(Device::isDisconnected)
    .forEach(Device::reconnect);
Run Code Online (Sandbox Code Playgroud)

  • 是的,它确实.有关更详细的说明,请阅读[this post](/sf/ask/1714146381/). (3认同)

Mak*_*oto 11

不要将代码重构为使用Streams.你没有获得任何好处,也没有比这样做更有优势,因为现在代码对于未来的维护者来说可读性较差,而且不那么惯用.

通过不使用流,您可以避免使用嵌套forEach语句.

请记住:流是为了更安全的并行化而无副作用. forEach根据定义,引入了副作用.你失去了流的好处并且同时失去了可读性,这使得它根本不那么令人满意.

  • 我认为通过ETO上面的回答"flatMap",流版本比非流版本更漂亮,更清晰. (8认同)
  • 了解Stream API的一种方法是知道何时*不*应用它.这感觉就像其中一次,特别是因为您不能保证您在流中执行的操作是无副作用的,这将使并行化*安全*. (5认同)