В чем преимущества потоков в Java перед циклами? [закрыто]

133

Меня спросили об этом на интервью, и я не уверен, что дал лучший ответ, который мог дать. Я упоминал, что вы можете выполнять параллельный поиск и что нулевые значения обрабатывались каким-то способом, который я не мог вспомнить. Теперь я понимаю, что думал о Optionals. Что мне здесь не хватает? Они утверждают, что это лучший или более сжатый код, но я не уверен, что согласен.


Учитывая, насколько кратко на него был дан ответ, кажется, что это был не слишком широкий вопрос.


Если они задают этот вопрос на собеседовании, а они явно задают этот вопрос, какой цели может служить его разбивка, кроме как затруднить поиск ответа? Я имею в виду, что ты ищешь? Я мог бы разбить вопрос и получить ответы на все подвопросы, но затем создать родительский вопрос со ссылками на все подвопросы ... хотя это кажется довольно глупым. Пока мы занимаемся этим, приведите, пожалуйста, мне пример менее широкого вопроса. Я не знаю, как задать только часть этого вопроса и все же получить содержательный ответ. Я мог бы задать точно такой же вопрос по-другому. Например, я мог бы спросить «Какую цель служат потоки?» или "Когда я буду использовать поток вместо цикла for?" или "Зачем беспокоиться о потоках, а не о циклах?" Но это все тот же вопрос.

... или это считается слишком широким, потому что кто-то дал действительно длинный многоточечный ответ? Откровенно говоря, любой знающий мог сделать это практически с любым вопросом. Если, например, вы являетесь одним из авторов JVM, вы, вероятно, могли бы говорить о циклах for весь день, в то время как большинство из нас не могли.

«Пожалуйста, отредактируйте вопрос, чтобы ограничить его конкретной проблемой с достаточной детализацией, чтобы найти адекватный ответ. Не задавайте сразу несколько разных вопросов. См. Страницу Как задать вопрос, чтобы прояснить этот вопрос».

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

user447607
источник
8
Это imho основано на мнении. Лично я предпочитаю потоки, потому что они делают код более читабельным. Это позволяет писать то, что вы хотите, а не как . Более того, делать удивительные вещи с однострочниками - это круто.
Arnaud Denoyelle
19
Даже если это 30 строк, один лайнер? Я не люблю длинные цепи.
user447607
1
Кроме того, все, что я здесь ищу, - это подходящий ответ на интервью. Это единственное «мнение», которое имеет значение.
user447607
1
С точки зрения образования, этот вопрос спас меня от некоторой деградации и на следующем собеседовании, @slim действительно справился с этим, но в промышленном отношении он также говорит о том, как языки программирования Microsoft построили свою карьеру на копировании языка Java, и, наконец, Java отомстила, разорвав вне лямбда-выражения и потоков от оппонентов, давайте посмотрим, что java собирается делать со структурами и союзами в будущем :)
ShayHaned
5
Обратите внимание, что потоки используют лишь часть возможностей функционального программирования: - /
Thorbjørn Ravn Andersen

Ответы:

253

Интересно, что вопрос интервью спрашивает о преимуществах, не спрашивая о недостатках, поскольку есть и то, и другое.

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

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

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

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Говорит «делаю петлю». Цель цикла глубже закопана в логике.

Потоки часто более краткие . Тот же пример показывает это. Тессер не всегда лучше, но если вы можете быть лаконичным и выразительным одновременно, тем лучше.

Потоки очень похожи на функции . В Java 8 представлены лямбда-выражения и функциональные интерфейсы, что открывает целый набор мощных методов. Потоки предоставляют наиболее удобный и естественный способ применения функций к последовательностям объектов.

Потоки способствуют меньшей изменчивости . Это как бы связано с аспектом функционального программирования - программы, которые вы пишете с использованием потоков, обычно относятся к тем программам, в которых вы не изменяете объекты.

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

Потоки могут лаконично выражать довольно сложное поведение . Например:

 stream.filter(myfilter).findFirst();

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

Потоки предоставляют возможности для повышения эффективности в будущем . Некоторые люди провели тестирование и обнаружили, что однопоточные потоки из in-memory Lists или массивов могут быть медленнее, чем эквивалентный цикл. Это правдоподобно, потому что в игре больше объектов и накладных расходов.

Но потоки масштабные. Помимо встроенной поддержки Java для параллельных потоковых операций, существует несколько библиотек для распределенного сокращения карты с использованием Streams в качестве API, потому что модель подходит.

Недостатки?

Производительность : forцикл через массив чрезвычайно легок с точки зрения использования кучи и ЦП. Если приоритетом является чистая скорость и бережливость памяти, использование потока хуже.

Знакомство . В мире полно опытных процедурных программистов, владеющих разными языками, для которых циклы знакомы, а потоки - в новинку. В некоторых средах вы захотите написать код, знакомый таким людям.

Когнитивные накладные расходы . Из-за его декларативного характера и повышенной абстракции от того, что происходит внутри, вам может потребоваться построить новую мысленную модель того, как код соотносится с выполнением. На самом деле вам нужно делать это только тогда, когда что-то идет не так, или если вам нужно глубоко проанализировать производительность или малозаметные ошибки. Когда он «просто работает», он просто работает.

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

тонкий
источник
4
Я думаю, было бы справедливо отметить, что потоковые вещи становятся все более распространенными и теперь появляются во многих часто используемых языках, которые не особенно ориентированы на FP.
Кейси
5
Учитывая перечисленные здесь плюсы и минусы, я думаю, что потоки НЕ стоят того ни для чего, кроме очень простых применений (небольшая логика if / then / else, не много вложенных вызовов или лямбда-выражений и т. Д.), В некритических частях производительности
Хенрик Кьюс Альстад
1
@HenrikKjusAlstad Это совершенно не тот вывод, который я намеревался сообщить. Потоки зрелые, мощные, выразительные и полностью подходят для промышленного кода.
slim
О, я не имел в виду, что не буду использовать его в продакшене. Но я бы предпочел использовать старомодные циклы / ifs и т. Д., А не потоки, особенно если результирующий поток будет выглядеть сложным. Я уверен, что есть варианты использования, в которых поток превосходит петли, и если это ясно, но чаще всего, я думаю, что это "привязано", а иногда даже наоборот. Таким образом, я бы добавил вес аргументам о когнитивных накладных расходах в пользу того, чтобы придерживаться старых способов.
Henrik Kjus Alstad
1
@lijepdam - но у вас все еще есть код, который говорит: «Я повторяю этот список (см. внутри цикла, чтобы узнать, почему)», когда «итерация по списку» не является основной целью кода.
slim
16

Помимо синтаксического удовольствия, Streams предназначены для работы с потенциально бесконечно большими наборами данных, тогда как массивы, коллекции и почти каждый класс Java SE, который реализует Iterable, полностью находятся в памяти.

Недостатком Stream является то, что фильтры, сопоставления и т. Д. Не могут генерировать проверенные исключения. Это делает Stream плохим выбором, скажем, для промежуточных операций ввода-вывода.

VGR
источник
7
Конечно, вы также можете перебирать бесконечные источники.
slim
Но если элементы для обработки сохраняются в БД, как вы используете потоки? У младшего разработчика может возникнуть соблазн прочитать их все в Коллекции, просто чтобы использовать Streams. И это было бы катастрофой.
Lluis Martinez
2
@LluisMartinez хорошая клиентская библиотека БД вернет что-то вроде Stream<Row>- или можно было бы написать собственную Streamреализацию, оборачивающую операции курсора результата БД.
slim
То, что Streams молча игнорирует исключения, - это ошибка, которую я недавно укусил. Неинтуитивный.
xxfelixxx
@xxfelixxx Потоки не игнорируют исключения молча. Попробуйте запустить это:Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
VGR
8
  1. Вы неправильно поняли: параллельные операции используют Streams, а не Optionals.

  2. Вы можете определить методы, работающие с потоками: принимать их как параметры, возвращать их и т. Д. Вы не можете определить метод, который принимает цикл как параметр. Это позволяет выполнять сложную потоковую операцию один раз и использовать ее много раз. Обратите внимание, что у Java здесь есть недостаток: ваши методы должны вызываться, а someMethod(stream)не собственные потоки stream.someMethod(), поэтому их смешивание усложняет чтение: попробуйте увидеть порядок операций в

    myMethod2(myMethod(stream.transform(...)).filter(...))

    Многие другие языки (C #, Kotlin, Scala и т. Д.) Допускают некоторую форму «методов расширения».

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

Алексей Романов
источник
Объясните 1. Разве Необязательный интерфейс не является средством, с помощью которого нули обрабатываются в цепочках? Что касается 3, это имеет смысл, потому что с короткозамкнутыми фильтрами метод будет вызываться только для определенных случаев. Эффективное. Имеет смысл сказать, что их использование снижает потребность в написании дополнительного кода, который необходимо будет протестировать и т. Д. После обзора я не уверен, что вы имеете в виду под последовательным регистром в 2.
user447607
1. Optionalявляется альтернативой null, но не имеет ничего общего с параллельными операциями. Если в вашем вопросе «Теперь я понимаю, что я думал о Optionals» речь идет только об nullобработке?
Алексей Романов
Я изменил порядок 2 и 3 и немного расширил их.
Алексей Романов
6

Вы перебираете последовательность (массив, коллекция, ввод, ...), потому что хотите применить некоторую функцию к элементам последовательности.

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

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

WERO
источник
4
Но дело не только в удобочитаемости. Код, который вам не нужно писать, - это код, который не нужно тестировать.
user447607
3
Кажется , что вы приезжаете с хорошими ответами для вашего интервью тоже
WERO
6

Я бы сказал, что его распараллеливание настолько простое в использовании. Попробуйте перебрать миллионы записей параллельно с циклом for. Мы переходим на многие процессоры, но не быстрее; так что чем проще будет работать параллельно, тем лучше, а с Streams это проще простого .

Что мне очень нравится, так это их многословие . Требуется немного времени, чтобы понять, что они на самом деле делают и производят, а не то, как они это делают.

Евгений
источник