it-swarm.com.ru

Каковы преимущества потоков над циклами в Java?

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


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


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

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

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

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

97
user447607

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

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

 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() управляет всей операцией, поэтому она эффективно останавливается после нахождения одного элемента.

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

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

Недостатки?

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

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

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

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

188
slim

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

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

9
VGR
  1. Вы неправильно поняли: параллельные операции используют Streams, а не Optionals.

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

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

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

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

8
Alexey Romanov

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

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

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

4
wero

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

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

2
Eugene