Создать поток Java с поставщиком lazyly

colymore спросил: 28 апреля 2018 в 09:23 в: java

возможно, с API-интерфейсом Java 8 stream, создать Stream, который не оценивается до тех пор, пока это не понадобится?

Я имею в виду.

У меня есть поток, который обрабатывает список элементы в одном из средних операций (карта), которые я должен прочитать, проходят через другой поток, и я хочу, чтобы этот поток в другой переменной использовался всеми другими объектами первого потока, но если нет объектов для обработки, я бы хотел чтобы избежать процесса второго потока.

Я думаю, что с кодом легче проверить:

Message[] process(@Nullable Message[] messages) {
    Stream<Function> transformationsToApply =
            transformations
                    .stream()
                    .filter(transformation -> transformationIsEnabled(transformation.getLeft()))
                    .map(Pair::getRight);    return Arrays.stream(messages != null ? messages : new Message[0])
            .filter(Objects::nonNull)
            .map(agentMessage -> {
                transformationsToApply.forEach(transformation -> processMessage(transformation, agentMessage));
                return agentMessage;
            })
            .toArray(Message[]::new);
}

Мое сомнение в том, что первое поколение потоков, я хотел бы вернуться поток, основанный на списке, который я обработал, но я хочу только сделать, если он будет использован (и использовать его для всех элементов сообщения).

Любая идея ..?

2 ответа

Есть решение
Sean Van Gorder ответил: 28 апреля 2018 в 03:19

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

Message[] process(@Nullable Message[] messages) {
    if (messages == null || messages.length == 0) return new Message[0];    List<Function> transformationsToApply = transformations.stream()
            .filter(transformation -> transformationIsEnabled(transformation.getLeft()))
            .map(Pair::getRight)
            .collect(Collectors.toList());    return Arrays.stream(messages)
            .filter(Objects::nonNull)
            .map(agentMessage -> {
                transformationsToApply.forEach(transformation -> processMessage(transformation, agentMessage));
                return agentMessage;
            })
            .toArray(Message[]::new);
}

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

Mạnh Quyết Nguyễn ответил: 28 апреля 2018 в 09:35

Не беспокойтесь, ваш transformationsToApply не оценивается до тех пор, пока вы не присоедините операцию терминала.

Промежуточные операции возвращают новый поток. Они всегда ленивы, выполнение промежуточной операции, такой как filter (), фактически не влияет на какую-либо фильтрацию, а создает новый поток, который при обращении содержит элементы исходного потока, которые соответствуют предикату. Обход источника конвейера не начинается, пока не выполняется терминальная работа конвейера.

colymore ответил: 28 апреля 2018 в 11:54
И значение обработанного потока будет использоваться для всех итераций? Или он будет рассчитываться каждый раз? Это мое сомнение, и именно поэтому я сохраняю его в переменной.
Mạnh Quyết Nguyễn ответил: 28 апреля 2018 в 12:55
Ваше выражение внутри .map приведет к тому, что исключение вызовет исключение, поскольку поток может быть использован только один раз. Я предлагаю вам сохранить результат transformationsToApply.toList в локальной переменной и повторно использовать его в методе .map