Java - фільтруйте потік за допомогою лямбда-виразів PlatoBlockchain Data Intelligence. Вертикальний пошук. Ai.

Java – фільтруйте потік за допомогою лямбда-виразів

Потоки Java були представлені ще в Java 8 у 2014 році, намагаючись ввести докладну мову Java у парадигму функціонального програмування. Java Streams надає багато гнучких і потужних функціональних операцій для виконання обробки колекції в одному рядку.

Фільтрування колекцій на основі певного предикату залишається однією з найбільш часто використовуваних функціональних операцій і може виконуватися за допомогою Predicate або стисло – з а Лямбда-вираз.

У цьому короткому посібнику ми розглянемо, як можна фільтрувати потік Java 8 за допомогою лямбда-виразів.

Фільтрування потоків у Java

Загалом будь-який Stream можна відфільтрувати за допомогою filter() метод і заданий предикат:

Stream filter(Predicate<? super T> predicate)

Кожен елемент у потоці виконується з предикатом і додається до вихідного потоку, якщо предикат повертає true. Ви можете надати a Predicate примірник:

Predicate contains = s -> s.contains("_deprecated");
List results = stream.filter(contains).collect(Collectors.toList());

Або спростіть його, надавши лямбда-вираз:

List results = stream.filter(s -> s.contains("_deprecated"))
                             .collect(Collectors.toList());

Або навіть згорнути лямбда-вираз у a посилання на метод:


List results = stream.filter(String::isEmpty)
                             .collect(Collectors.toList());

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

Пам'ятайте, що потоки не є колекціями – це потоки колекцій, і вам доведеться зібрати їх назад у будь-яку колекцію, наприклад a List, Map, тощо, щоб надати їм постійності. Крім того, усі операції виконуються над елементами потоку проміжний or термінал:

  • Проміжні операції повертають новий потік зі змінами від попередньої операції
  • Операції терміналу повертають тип даних і призначені для завершення конвеєра обробки в потоці

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

Примітка: Навіть при об’єднанні багатьох лямбда-виразів у вас можуть не виникнути проблеми з читабельністю за належних розривів рядків.

У наступних прикладах ми працюватимемо з цим списком книг:

Book book1 = new Book("001", "Our Mathematical Universe", "Max Tegmark", 432, 2014);
Book book2 = new Book("002", "Life 3.0", "Max Tegmark", 280, 2017);
Book book3 = new Book("003", "Sapiens", "Yuval Noah Harari", 443, 2011);
        
List books = Arrays.asList(book1, book2, book3);

Фільтр колекції за допомогою Stream.filter()

Давайте відфільтруємо цю збірку книг. Підходить будь-який предикат – отже, наприклад, давайте відфільтруємо, які книги мають понад 400 сторінок:

List results = books.stream()
                          .filter(b -> b.getPageNumber() > 400)
                          .collect(Collectors.toList());

Це призводить до списку, який містить:

[
Book{id='001', name='Our Mathematical Universe', author='Max Tegmark', pageNumber=432, publishedYear=2014}, 
Book{id='003', name='Sapiens', author='Yuval Noah Harari', pageNumber=443, publishedYear=2011}
]

Під час фільтрації дуже корисним є метод з’єднання map(), що дозволяє вам зіставляти об’єкти з іншими значеннями. Наприклад, ми можемо зіставити кожну книгу з її назвою і таким чином повернути лише Імена книг, які відповідають присудку з filter() дзвінок:

List results = books.stream()
                            .filter(b -> b.getPageNumber() > 400)
                            .map(Book::getName)
                            .collect(Collectors.toList());

Це призводить до списку рядків:

[Our Mathematical Universe, Sapiens]

Фільтрувати колекцію кількох предикатів за допомогою Stream.filter()

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

 List results = books.stream()
                    .filter(b -> b.getPageNumber() > 400 && b.getName().length() > 10)
                    .collect(Collectors.toList());
                    


 List results2 = books.stream()
                    .filter(b -> b.getPageNumber() > 400)
                    .filter(b -> b.getName().length() > 10)
                    .collect(Collectors.toList());

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

При використанні кількох критеріїв лямбда-виклики можуть бути дещо тривалими. На цьому етапі вилучення їх як автономних предикатів може запропонувати більше ясності. Але який підхід швидше?

Один фільтр із складною умовою чи кілька фільтрів?

Це залежить від вашого обладнання, розміру вашої колекції та того, чи використовуєте ви паралельні потоки чи ні. Загалом один фільтр із складною умовою перевершить кілька фільтрів із простішими умовами (малі та середні колекції) або працюватиме на тому самому рівні (дуже великі колекції). Якщо ваші умови надто довгі, вам може бути корисно розподілити їх між кількома filter() виклики, для кращої читабельності, оскільки продуктивність дуже схожа.

Найкращий вибір — спробувати обидва, зверніть увагу на продуктивність цільовий пристрійі відповідно відкоригуйте свою стратегію.

Користувач GitHub volkodavs виконав контрольний тест фільтрації в операціях пропускної спроможності та розмістив результати на «javafilters-бенчмарки» сховище. Результати зведені в інформаційну таблицю:

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

Часова мітка:

Більше від Stackabuse