Использует ли последовательный поток в Java 8 параметр объединителя при вызове сбора?

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


person Graeme Moss    schedule 13.06.2014    source источник
comment
Что происходит, когда вы передаете null для объединителя? Какой вывод вы делаете из этого эксперимента?   -  person JB Nizet    schedule 13.06.2014
comment
@JBNizet Это вызовет исключение NullPointerException.   -  person johnlinp    schedule 27.05.2020


Ответы (1)


Имейте в виду, что нужно разрабатывать в соответствии со спецификациями интерфейса, а не с реализацией. Реализация может измениться в следующей версии Java, тогда как спецификация должна остаться неизменной.

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

List<String> tokens = ...;
String result = tokens.stream().reduce("", String::concat, String::concat);
person nosid    schedule 13.06.2014
comment
Было бы очень разумно, если бы реализация Stream распознавала, может ли использование объединителя повысить производительность. Однако больший риск заключается в том, что разработчик сам меняет поток на параллельный в то время, когда он забыл об опущенном объединителе… - person Holger; 13.06.2014
comment
@Holger Правильно, или другой разработчик передает поток вашему коду, и этот поток может измениться на параллельный в будущем. - person Stuart Marks; 13.06.2014
comment
Спасибо за Ваш ответ. У меня была такая же проблема, и, всегда придерживаясь разработки против интерфейса, я жалею, что нет простой версии сборщика без объединителя. Это сделало бы вещи НАМНОГО проще. Они могли бы сделать Collector.Characteristics, чтобы обозначить это, или более простую версию интерфейса Collector... - person glglgl; 03.09.2014
comment
Еще один вопрос: здесь я нашел предложение A sequential implementation of a reduction using a collector would create a single result container using the supplier function, and invoke the accumulator function once for each input element. Разве это уже не указывает на то, что объединитель не используется в этой ситуации? - person glglgl; 03.09.2014
comment
@Holger, @nosid Не совсем понял, как упомянутый вами в примере объединитель повысит производительность в последовательном потоке. Насколько я понимаю, в любой момент времени в потоке есть строка аккумулятора и элемент. Мы просто объединяем их и создаем новую строку-аккумулятор. Я что-то упускаю? - person GeekFactory; 21.11.2019
comment
@GeekFactory вы упускаете из виду затраты на «просто конкатирование». Конкатенация строк подразумевает создание новой строки, которая является копией обеих строк аргументов. Чем длиннее строка, тем больше накладных расходов. А промежуточным результатом объединения нескольких строк является строка, которая становится все длиннее и длиннее. Вы можете поискать «Алгоритм художника Шлемиэля» для получения интуитивно понятной картины. См. также ideone.com/0kRRnt Просто чтобы было понятно, collect(joining()) все еще лучше, но я надеюсь, вы понимаете , использование объединителя снижает накладные расходы в этом примере. - person Holger; 21.11.2019
comment
@Холгер Вау! Это отличный пример, чтобы продемонстрировать, как объединитель имеет смысл в последовательных потоках. Если я не ошибаюсь, return reduce.apply(......) работает как объединитель, верно? Также есть имя для этого алгоритма, который вы используете? - person GeekFactory; 21.11.2019
comment
Именно @GeekFactory на данный момент функция действует как объединитель. Это эквивалентно тому, что делает Stream API при разделении рабочей нагрузки на параллельные потоки. Когда дело доходит до имени, я бы сказал, что это вариант разделяй-и- Победить. - person Holger; 21.11.2019
comment
Не могли бы вы предоставить подробный анализ или примеры, показывающие, как это улучшает производительность? Или ссылки на статьи/исследования? - person Vyacheslav Babanin; 28.03.2021