Используйте функцию карты потока Java 8 для долгосрочных задач - PullRequest
1 голос
/ 28 октября 2019

У меня есть метод, который принимает список URL-адресов (удаленных файлов) в качестве параметра, который должен быть загружен. Метод возвращает список другого типа (называемый Attachment), который на самом деле содержит свойство java File-type внутри. Для этого случая я использовал API-интерфейс Java Stream, чтобы перебрать URL-адреса и начать загрузку в функции «map», которая затем возвращает экземпляр Attachment.

Теперь мой вопрос: злоупотребляю ли я API-интерфейсом Java Streamдля вещей, которые его не предназначены для? Как положить в нее долго выполняющиеся задачи? Должен ли я просто выполнять небольшие операции с входными данными?

Единственный минус, который я сейчас вижу, состоит в том, что его немного сложнее проверить.

private List<Attachment> download(List<URL> attachments) {
        return attachments.stream().map(attachmentUrl -> {
            try {
                Attachment attachment = new Attachment();
                File attachmentFile = new File(getFilename(attachment.getAttachmentId(), attachmentUrl));
                FileUtils.copyURLToFile(
                        attachmentUrl,
                        attachmentFile,
                        CONNECT_TIMEOUT,
                        READ_TIMEOUT);
                attachment.setAttachmentFile(attachmentFile);
                return attachment;
            } catch (IOException e) {
                e.printStackTrace();
                LOGGER.error(e.getLocalizedMessage());
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

Ответы [ 2 ]

1 голос
/ 28 октября 2019

Я думаю, что было бы полезно подумать о map и других функциональных конструкциях (например, filter, reduce и т. Д.) Не столько как о функциях, сколько о синтаксисе . stream().map() - это синтаксис, который выполняет функциональный эквивалент цикла for. На вопрос "злоупотребляю ли я этим синтаксисом из-за чего я использую его для выполнения?"в этом случае менее значим: циклы for не заботятся о том, сколько времени занимают задачи, выполняемые на каждой итерации, и map. Она не зависит от операции , которую она применяет, поэтому единственный вопрос заключается в том, правильно ли вы используете синтаксис, то есть циклически перебираете коллекцию, отображаете из чего-то в нечто.

В этом контексте, где map является синтаксисом, с вашими желаемыми операциями все в порядке. Тем не менее, ваша реализация может быть немного очищена.

attachmentUrl -> {
    try {
        Attachment attachment = new Attachment();
        File attachmentFile = new File(getFilename(attachment.getAttachmentId(), attachmentUrl));
        FileUtils.copyURLToFile(
                attachmentUrl,
                attachmentFile,
                CONNECT_TIMEOUT,
                READ_TIMEOUT);
        attachment.setAttachmentFile(attachmentFile);
        return attachment;
    } catch (IOException e) {
        e.printStackTrace();
        LOGGER.error(e.getLocalizedMessage());
    }
    return null;
}

Немного велика для встроенной map лямбды. В целом, я склонен скептически, хотя и не всегда, неодобрительно относиться к любой map лямбде, которая требует фигурных скобок, то есть занимает более одной строки. Я бы предложил рефакторинг этой лямбды в именованную функцию и, возможно, пару, которые либо вложены (map(this::A), где A затем вызывает B), либо используются последовательно вашими потоковыми операциями map(this::A).map(this::B).


[РЕДАКТИРОВАТЬ:] Что касается распараллеливания вашего stream: имейте в виду, что вы делаете больше, чем просто процессор, как часть этого метода - вы, кажется, делаете сетевые операции ввода-вывода и файл IO. Если вы выполняете параллельно, вы будете распараллеливать не только загрузку вашего процессора, но и вашу сеть и использование диска. Если доминирующим фактором является сетевой диск или , а не центральный процессор, то распараллеливание, скорее всего, принесет вам очень мало и может ухудшить ситуацию. В общем, больше потоков! = Более быстрое чтение или запись по сети или с диска. этот вопрос о параллельном вводе-выводе может оказаться полезным.

1 голос
/ 28 октября 2019

Потоки - очень элегантный инструмент для работы с данными в функциональном программировании, один и тот же ввод приведет к тому же выводу без побочных эффектов, это сделает ваш код менее подверженным ошибкам и более читаемым. Таким образом, нет никакого злоупотребления с точки зрения использования независимо от размера ввода. Вы можете использовать параллельные потоки, если ожидаете иметь дело с огромным количеством данных. Однако ваша реализация может использовать небольшую очистку, не делегировать всю бизнес-логику одной операции карты, сделать ее более детальной и распределить логику по нескольким картографам, вы можете объявить картографы как любую переменную Function<URL, File> urlToFileMapper = url -> {...} и подключитькартограф в потоке, attachments.stream().map(urlToFileMapper).map(anotherDeclaredMapper)...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...