У Java які переваги потоків над петлями? [зачинено]


133

Мене це запитали на інтерв'ю, і я не переконаний, що я дав найкращу відповідь, яку я міг мати. Я згадував, що ви можете робити паралельний пошук і що нульові значення оброблялися якимось чином я не міг запам'ятати. Тепер я розумію, що думав про необов’язковість. Що я тут пропускаю? Вони стверджують, що це кращий чи більш стислий код, але я не впевнений, що згоден.


Зважаючи на те, наскільки лаконічно відповіли, здається, що це було не надто широке питання.


Якщо вони задають це питання на співбесіді, і явно вони є, то яка мета може зруйнувати його, крім того, щоб ускладнити пошук відповіді? Я маю на увазі, що ти шукаєш? Я міг би розбити питання і відповісти на всі підпитання, але потім створити батьківське запитання із посиланнями на всі підпитання ... Хоча, здається, досить нерозумно. Поки ми на цьому, будь ласка, наведіть мені приклад менш широкого питання. Я не знаю жодного способу задати лише частину цього питання і все одно отримати змістовну відповідь. Я міг задати абсолютно те саме питання по-іншому. Наприклад, я можу запитати "Якій цілі служать потоки?" або "Коли я використовую потік замість циклу?" або "Навіщо турбуватися потоками замість циклів?" Це все точно те саме питання.

... чи це вважається занадто широким, тому що хтось дав дійсно довгу багатоточну відповідь? Відверто кажучи, хтось, хто знає, міг це зробити практично з будь-яким питанням. Якщо ви, наприклад, є одним з авторів JVM, ви, напевно, могли б цілий день говорити про петлі, коли більшість із нас не змогли.

"Будь-ласка, відредагуйте це питання, щоб обмежити його конкретною проблемою з достатньою деталізацією, щоб визначити адекватну відповідь. Уникайте задавати декілька чітких питань одночасно. Див. Сторінку" Як задати ", щоб допомогти уточнити це питання."

Як зазначалося нижче, була надана адекватна відповідь, яка доводить, що вона існує і що її досить легко надати.


7
Це ґрунтується на думці. Особисто я віддаю перевагу потокам, оскільки це робить код читабельнішим. Це дозволяє написати, що ви хочете, а не як . Більше того, робити цілком дивовижні речі з одноклассником абсолютно недобре.
Арно Денойель

19
Навіть якщо це лінія 30, один лайнер? Мені не подобаються довгі ланцюги.
user447607

1
Крім того, все, що я тут шукаю, - це відповідна відповідь на співбесіду. Це єдина "думка", яка має значення.
user447607

1
Якщо говорити в освіті, це питання врятувало мене деградацією і в майбутньому інтерв'ю, але @slim дійсно прибив це, але в індустріальному відношенні він також говорить про те, як мови програмування Microsoft будували свою кар’єру на зриванні мови Java, і, нарешті, Java помститься від видобутку. off Lambda Expression та потоки від супротивників, давайте подивимось, що ява збирається робити зі Структурами та Союзами в майбутньому :)
ShayHaned

5
Зауважте, що потоки використовуються лише частину потужності в функціональному програмуванні: - /
Thorbjørn Ravn Andersen

Відповіді:


251

Цікаво, що питання інтерв'ю задає питання про переваги, не питаючи про недоліки, бо є і те, і інше.

Потоки - декларативніший стиль . Або більш виразний стиль. Можна вважати за краще заявити про свій намір у коді, ніж описувати, як це робиться:

 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()керує цілою операцією, тому вона ефективно зупиняється після пошуку одного предмета.

Потоки дають можливість майбутнього підвищення ефективності . Деякі люди зробили орієнтир і виявили, що однопотокові потоки з пам'яті Listабо масивів можуть бути повільнішими, ніж еквівалентний цикл. Це правдоподібно, оскільки в грі більше об'єктів та накладних витрат.

Але масштаби потоків. Як і вбудована підтримка Java для паралельних потокових операцій, існує кілька бібліотек для розподіленого зменшення карт за допомогою Streams як API, оскільки модель підходить.

Недоліки?

Продуктивність : forЦикл через масив надзвичайно легкий, як з точки зору нагромадження, так і з використанням процесора. Якщо швидкість сировини та ощадливість пам’яті є пріоритетом, використання потоку гірше.

Ознайомлення . Світ наповнений досвідченими процедурними програмістами з багатьох мовних груп, для яких знайомі петлі, а потоки - нові. У деяких середовищах ви хочете написати код, знайомий цій людині.

Пізнавальний наклад . Через декларативний характер та посилену абстракцію від того, що відбувається під ним, можливо, вам знадобиться побудувати нову ментальну модель того, як код стосується виконання. Насправді вам потрібно це робити лише тоді, коли все піде не так, або якщо вам потрібно глибоко проаналізувати продуктивність або тонкі помилки. Коли це "просто працює", воно просто працює.

Налагоджувачі покращуються, але навіть зараз, коли ви переходите через потіковий код у відладчику, це може бути важче працювати, ніж еквівалентний цикл, тому що простий цикл дуже близький до змінних та кодових локацій, з якими працює традиційний налагоджувач.


4
Я думаю, було б справедливо чітко говорити про те, що подібні до потоку речі стають набагато звичнішими і зараз з’являються у багатьох поширених мовах, які не особливо орієнтовані на FP.
Кейсі

5
З огляду на плюси та мінуси, перелічені тут, я думаю, що потоки НЕ варті нічого іншого, крім дуже простого використання (мало логіки, якщо / то / ще, не багато вкладених дзвінків чи лямбда тощо), у некритичних частинах виконання
Генрік Кюс Альстад

1
@HenrikKjusAlstad Це абсолютно не той спосіб, з яким я мав намір спілкуватися. Потоки є зрілими, потужними, виразними і цілком підходять для коду виробництва.
струнка

О, я не мав на увазі, що я не буду використовувати його у виробництві. Але, швидше, я б замовчував старі старомодні петлі / ifs тощо, а не потоки, особливо якщо поточний результат буде виглядати складним. Я впевнений, що існують випадки, коли потік буде бити петлі, і якщо він чітко, але частіше за все, я думаю, що він "прив'язаний", а іноді навіть навпаки. Таким чином, я поставив би вагу когнітивного накладного аргументу за дотримання старих способів.
Генрік Кюс Альстад

1
@lijepdam - але ви все одно будете мати код, який говорить "Я повторюю цей список (див. всередині циклу, щоб дізнатися чому)", коли "повторення над списком" не є основною метою коду.
струнка

16

Синтаксична розвага убік, потоки призначені для роботи з потенційно нескінченно великими наборами даних, тоді як масиви, колекції та майже кожен клас Java SE, який реалізує Iterable, повністю знаходяться в пам'яті.

Недоліком потоку є те, що фільтри, відображення тощо не можуть кидати перевірені винятки. Це робить Потік поганим вибором для, скажімо, проміжних операцій вводу / виводу.


7
Звичайно, ви також можете перебирати нескінченні джерела.
струнка

Але якщо елементи, які обробляються, зберігаються в БД, як ви використовуєте потоки? Молодший розробник міг би спокусити прочитати їх у Колекції лише для використання потоків. І це було б лихом.
Lluis Martinez

2
@LluisMartinez хороша бібліотека клієнтів БД поверне щось на зразок Stream<Row>- або можна було б написати власну Streamреалізацію, обертаючи операції з курсором результатів БД.
струнка

Те, що потоки мовчки ігнорують винятки - це помилка, яку мене нещодавно покусав. Неінтуїтивні.
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, але вона не має нічого спільного з паралельними операціями. Якщо тільки "Тепер я розумію, що я думав про необов'язкові", у вашому питанні йдеться лише про nullповодження?
Олексій Романов

Я змінив порядок 2 і 3 і трохи розширив їх обоє.
Олексій Романов

6

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

Потоки дають вам можливість складати функції на елементах послідовності і дозволяють реалізувати найбільш поширені функції (наприклад, картографування, фільтрування, пошук, сортування, збирання, ...) незалежно від конкретного випадку.

Тому, даючи певну задачу циклу, у більшості випадків ви можете виразити її меншим кодом за допомогою потоків, тобто ви отримаєте читабельність .


4
Що ж, це не просто читабельність. Код, який вам не потрібно писати, - це код, який ви не повинні тестувати.
user447607

3
Мабуть, ви також знайдете хороші відповіді і для свого інтерв'ю
wero

6

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

Мені дуже подобається багатослівність, яку вони пропонують. Мало часу, щоб зрозуміти, що вони насправді роблять і виробляють, на відміну від того, як вони це роблять.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.