JavaRush /Java блог /Random UA /Непрості прості потоки
Oleg Savenko
41 рівень
Одесса

Непрості прості потоки

Стаття з групи Random UA
З кожною новою версією Java стає все багатшим і багатшим. Хоча найчастіше основа багатств закладено у ранніх версіях, але мова продовжував удосконалюватися як і методології, і у реалізації. Колекції Java не є винятком. Основний каркас колекцій з'явився у версії J2SE 1.2 і продовжував розвиватися, зазнаючи змін, які більше радували, ніж засмучували. З виходом JDK 5 колекції стали зручнішими, швидше, а працювати з ними стало легше. Це призвело до того, що програмісти почали інтенсивніше їх експлуатувати. Виробабося деякі патерни по роботі з ними, які без жодного сумніву ефективні. Але з появою JDK 8 колекції знову стали кращими, а краще вони стали завдяки потокам. Очевидно, завдяки тому, що колекцію можна подати у вигляді потоків, змінюватиметься і методика роботи з ними. Тому я хочу показати, як звичні та зрозумілі рішення з колекціями стають ще простішими.

Приклад 1. Простіше не буває

Звичайно почнемо з найпростішого, пробіжимося всіма елементами колекції і виведемо всі елементи.
// создадим и заполним список
   List<Integer> list = new ArrayList<>();
   Collections.addAll(list, 1, 5, 6, 11, 3, 15, 7, 8);
   // а теперь
   // быстрый for по всем елементам, только для коллекций
   for (Integer i:list){
       System.out.println(i);
   }
   //но мы уже живем в JDK 8
   //а значит нужно так
   list.stream().forEach(System.out::println);
Як ви помітабо, є новий синтаксис, який, на мій погляд, новачка Java, набагато простіше. Отже, що видно у новому синтаксисі:
берем_список(list).превращаем_в_поток(stream).перебираем_все_элементы(forEach)(тут_интуитивно_понятно)
System.out::println— посилання на статичний метод, який виводить рядок на консоль Замість посилання на статичний метод можна використовувати трохи інший, поки що менш зрозумілий запис:
list.stream().forEach(i -> System.out.println(i));
у цьому записі використовується лямбда-вираз. І так, щоб навчитися працювати з потоками, потрібно буде вивчити лямбда-вирази – вони чудові. Далі я не показуватиму як працювати з колекціями використовуючи тільки потоки, розраховуючи на те, що з традиційними способами ви познайомабося в ході курсу.

Приклад 2. Знайдемо парні значення у списку та виведемо їх у консоль

list.stream().filter(i -> i%2==0).forEach(System.out::println);
Все завдання вирішено в один рядок. Сподіваюся вам подобається працювати в один рядок. За допомогою методу filterми профільтрували потік і те, що залишилося, вивели на консоль. Фільтр дуже потужна штука, яка здатна допомогти у найнесподіваніших випадках. Давайте подивімося і змінимо умови завдання. Наприклад, нам потрібно порахувати скільки парних чисел у списку:
long count = list.stream().filter(i -> i%2==0).count();
І знову в один рядок. Якось здається, все просто. У фільтрі ми використовували лямбда-вираз, тим самим помістивши в новий потік лише парні числа, а потім до нового потоку застосували count, який і вважав, скільки елементів у новому потоці.

Приклад 3. Порахуємо скільки слів у списку має довжину 5 символів

Пограли з цілими числами, тепер пограємося зі словами.
List<String> list = new ArrayList<>();
Collections.addAll(list, "разые", "слова", "интересные", "і", "ні", "дуже");

System.out.println(list.stream().filter(w -> w.length() == 5).count());
Ми знову скористалися фільтром. У фільтрі за допомогою лямбда-вираження вивели новий потік, а далі зрозумілий countпорахував, скільки в новому потоці елементів.

Приклад 4. Вивести унікальні слова

Знайоме завдання, коли ми прочитали в колекцію з файлу багато різних слів, а тепер нам потрібні унікальні.
List<String> list = new ArrayList<>();
Collections.addAll(list, "Вася", "Таня", "Оля", "Вася", "Оля", "Сергей");

list.stream().distinct().forEach(System.out::println);
Основна дія була зроблена над потоком за допомогою distinct. Далі я пропоную подивитись деякі наші завдання з курсу за допомогою потоків

Приклад 5. Довгі слова

На 9-му рівні Java Core, в 11 лекції є цікаве завдання, в ній потрібно записати через кому в Файл2 слова, довжина яких строго більше 6. Який би город не городабо, я пропоную такий хід рішення:
  1. З файлу джерела читаємо усі слова до списку.

  2. Потім виконуємо наступний рядок

    Optional<String> rezult = list.stream()
    				.filter(w->w.length()>6)
    				.reduce((w1,w2)->w1+", "+w2);
  3. result.get()записуємо у файл.

Ось таке цікаве рішення за допомогою потоків. Фільтр профільтрував, а за допомогою reduce та регулярного виразу сформували необхідний рядок.

Приклад 6. Слова з цифрами

Записати через пробіл у файл2 всі слова, які містять цифри, наприклад, а1 або abc3d. Це теж умова з нашого задачника, як ви здогадалися, рішення просте.
Optional<String> rezult = list.stream()
				.filter(w->w.matches(".*?\\d+.*?"))
				.reduce((w1,w2)->w1+" "+w2);
Профільтрували потік за допомогою регулярного виразу, а потім reduce та лямбда-вираз. Як багато спільного із попереднім завданням.

Приклад 7. Виділяємо числа

Все завдання звучить так:
  • Рахувати з консолі 2 імені файлу.
  • Вивести у другий файл усі числа, які є у першому файлі.
  • Числа виводити через пропуск.
  • Закрити потоки.
Для основного завдання, а це запис чисел у рядок через пробіл, для подальшого запису у файл, я пропоную застосувати потік, виглядатиме це так:
Optional<String> rezult = list.stream().filter(w->w.matches("\\d+"))
				.reduce((w1,w2)->w1+" "+w2);
System.out.println(rezult.get());
За допомогою потоків завдання вирішується дуже простим способом. Але, диво, порівняйте це рішення з двома попередніми самостійно. Завершуючи статтю, я хочу Вам зізнатися: хлопці, я шахраював, коли підбирав приклади. Справа в тому, що я вибрав найпростіші приклади, щоб показати Вам на знайомих завданнях незнайому тему. Звичайно, потоки використовуються як для простих, так і для складніших завдань. Але як підкреслив у своїй книзі Хорстман «Потоки даних діють за принципом «що, а не як робити», а отже багато, раніше складних завдань, можуть стати більш простими. Не знаю як Вам, але мені потоки сподобалися, сподіваюся, я не відбив у Вас бажання їх повчити. І ще дещо поясню:
  1. Я не ставив собі за мету навчити читача використовувати потоки в колекціях, не настільки я досвідчений наставник. Я хотів показати, що потоки це просто і дуже цікаво! Взагалі, це необхідність.
  2. Чим більше я розумію потоки, тим більше бачу завдань, де вони вирішують усі проблеми із задачника курсу, а отже, потоки придумали недаремно.
  3. З потоками не тільки просто працювати, але вони ще мають важливу перевагу - при роботі з великими масивами даних потоки частіше продуктивніші.
PS . Про ВСІ ПРОБЛЕМИ я пожартував. Рекомендую: Java бібліотека професіонала. Том 2 Розширені засоби програмування. Кей Хорстманн. Маю десяте видання.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ