Как избежать вложенных вызовов forEach? - PullRequest
0 голосов
/ 18 декабря 2018

У меня есть следующий код:

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();
        }
    }
}

Я хочу реорганизовать код с помощью Stream API.Моя первая попытка была похожа на следующее:

gateways
    .stream()
    .forEach(
        gateway -> {
            gateway
                .getDevices()
                .parallelStream()
                .filter(device -> device.isDisconnected())
                .forEach(device -> device.reconnect())
            ;
        }
    )
;

Мне не понравилось, поэтому после некоторых модификаций я получил следующий код:

gateways
    .parallelStream()
    .map(gateway -> gateway.getDevices().parallelStream())
    .map(stream -> stream.filter(device -> device.isDisconnected()))
    .forEach(stream -> stream.forEach(device -> device.reconnect()))
;

Мой вопрос: есть лиспособ избежать вложенности forEach.

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Я бы попробовал это с последовательным потоком, прежде чем использовать параллельный:

gateways
    .stream()
    .flatMap(gateway -> gateway.getDevices().stream())
    .filter(device -> device.isDisconnected())
    .forEach(device ->  device.reconnect())
;

Идея состоит в том, чтобы создать поток через gateways.stream(), а затем сгладить последовательности, возвращенные из gateway.getDevices() через flatMap.

Затем мы применяем операцию filter, которая действует как оператор if в вашем коде, и, наконец, операцию терминала forEach, позволяющую нам вызывать reconnect на каждом устройстве, проходящем через фильтр.операция.

см. Должен ли я всегда использовать параллельный поток, когда это возможно?

0 голосов
/ 18 декабря 2018

Не рефакторинг вашего кода для использования потоков.Вы не получаете никаких преимуществ и не получаете никаких преимуществ по сравнению с этим, поскольку код теперь менее читабелен и менее идиоматичен для будущих сопровождающих.

Не используя потоки, вы избегаете вложенных операторов forEach.

Помните: потоки не должны иметь побочных эффектов для более безопасного распараллеливания.forEach по определению вводит побочные эффекты .Вы теряете преимущество потоков и одновременно теряете читаемость, что делает это менее желательным для всех.

0 голосов
/ 18 декабря 2018

Вы должны сгладить поток потоков, используя flatMap вместо map:

gateways
    .parallelStream()
    .flatMap(gateway -> gateway.getDevices().parallelStream())
    .filter(device -> device.isDisconnected())
    .forEach(device -> device.reconnect());

Я бы улучшил его, используя ссылки на методы вместо лямбда-выражений:

gateways
    .parallelStream()
    .map(Gateway::getDevices)
    .flatMap(List::stream)
    .filter(Device::isDisconnected)
    .forEach(Device::reconnect);
...