DistinctBy в Java Stream API

1. Огляд

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

У цій статті ми покажемо різні альтернативи фільтруванню колекції за допомогою певного атрибута об’єктів у списку.

2. Використання Stream API

API Stream надає метод distinct (), який повертає різні елементи списку на основі методу equals () класу Object .

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

2.1. Використання фільтру у формі стану

Одним з можливих рішень було б запровадити функціональний предикат:

public static  Predicate distinctByKey( Function keyExtractor) { Map seen = new ConcurrentHashMap(); return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; }

Для тестування ми використаємо такий клас Person, який має атрибути вік , електронна пошта та ім’я:

public class Person { private int age; private String name; private String email; // standard getters and setters }

І щоб отримати нову відфільтровану колекцію за назвою , ми можемо використовувати:

List personListFiltered = personList.stream() .filter(distinctByKey(p -> p.getName())) .collect(Collectors.toList());

3. Використання колекцій Eclipse

Eclipse Collections - це бібліотека, яка надає додаткові методи обробки потоків та колекцій на Java.

3.1. Використання ListIterate.distinct ()

Метод ListIterate.distinct () дозволяє фільтрувати потік, використовуючи різні HashingStrategies. Ці стратегії можна визначити, використовуючи лямбда-вирази або посилання на методи.

Якщо ми хочемо відфільтрувати за іменем Особи :

List personListFiltered = ListIterate .distinct(personList, HashingStrategies.fromFunction(Person::getName));

Або, якщо атрибут, який ми збираємось використовувати, є примітивним (int, long, double), ми можемо використовувати спеціалізовану функцію, таку як:

List personListFiltered = ListIterate.distinct( personList, HashingStrategies.fromIntFunction(Person::getAge));

3.2. Залежність Мавена

Нам потрібно додати наступні залежності до нашого pom.xml, щоб використовувати колекції Eclipse у нашому проекті:

 org.eclipse.collections eclipse-collections 8.2.0 

Ви можете знайти останню версію бібліотеки Eclipse Collections у сховищі Maven Central.

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

4. Використання Vavr (J avaslang )

Це функціональна бібліотека для Java 8, яка забезпечує незмінні дані та функціональні структури управління.

4.1. Використання List.distinctBy

Для фільтрування списків цей клас забезпечує власний клас List, який має метод distinctBy (), що дозволяє фільтрувати за атрибутами об’єктів, які він містить:

List personListFiltered = List.ofAll(personList) .distinctBy(Person::getName) .toJavaList();

4.2. Залежність Мавена

Ми додамо наступні залежності до нашого pom.xml, щоб використовувати Vavr у нашому проекті.

 io.vavr vavr 0.9.0 

Ви можете знайти останню версію бібліотеки Vavr у центральному сховищі Maven.

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

5. Використання StreamEx

Ця бібліотека надає корисні класи та методи обробки потоків Java 8.

5.1. Використання StreamEx.distinct

У межах класів є StreamEx, який має чіткий метод, до якого ми можемо надіслати посилання на атрибут, де ми хочемо виділити:

List personListFiltered = StreamEx.of(personList) .distinct(Person::getName) .toList();

5.2. Залежність Мавена

Ми додамо наступні залежності до нашого pom.xml, щоб використовувати StreamEx у нашому проекті.

 one.util streamex 0.6.5 

Ви можете знайти останню версію бібліотеки StreamEx у сховищі Maven Central.

6. Висновок

У цьому короткому навчальному посібнику ми розглянули приклади отримання різних елементів потоку на основі атрибута за допомогою стандартного API Java 8 та додаткових альтернатив з іншими бібліотеками.

Як завжди, повний код доступний на GitHub.