Фильтрация вложенных коллекций с помощью Stream в Java

Java Stream API обеспечивает превосходную возможность фильтрации элементов в Stream. Иногда элементы потока также содержат вложенные коллекции; нам нужно отфильтровать эти элементы во вложенных коллекциях. В этом руководстве по Java обсуждаются различные подходы с примерами для применения фильтрации к вложенным коллекциям и сбора отфильтрованных выходных элементов в новую коллекцию.

1. Настройка

Давайте создадим некоторые фиктивные данные, близкие к реальным приложениям, чтобы сделать пример более реалистичным. В этом примере у нас есть два класса AccountStatement и Transaction. Каждая выписка по счету связана со списком транзакций с различными суммами. Мы применим фильтрацию к суммам транзакций.

@Data@AllArgsConstructorclass AccountStatement {private long id;private String accountNumber;private List<Transaction> transactions;}@Data@AllArgsConstructorclass Transaction {private long id;private double amount;private String accountNumber;}

Далее мы добавили следующие фиктивные данные. В выписках по счетам 1 и 3 есть транзакции(по одной на каждую) стоимостью более 500. Мы применим фильтрацию к этим записям.

List<AccountStatement> accStmts = List.of(new AccountStatement(1, "A001", List.of(new Transaction(1, 100, "A001"),new Transaction(2, 60, "A001"),new Transaction(3, 550, "A001"))), // More than 500new AccountStatement(2, "A002", List.of(new Transaction(4, 200, "A002"),new Transaction(5, 160, "A002"),new Transaction(6, 100, "A002"))),new AccountStatement(3, "A003", List.of(new Transaction(7, 10, "A003"),new Transaction(8, 20, "A003"),new Transaction(9, 3040, "A003")))); // More than 500

2. Получение списка транзакций

Сначала давайте рассмотрим несколько примеров фильтрации списка транзакций на сумму более 500.

2.1. Использование flatMap() и filter()

Это довольно простой метод. В этом подходе мы сглаживаем вложенные коллекции в один поток с помощью ' flatMap ', а затем применяем условие фильтрации с помощью 'filter'.

List<Transaction> transactionsMoreThan500 = accStmts.stream().flatMap(stmt -> stmt.getTransactions().stream()).filter(transaction -> transaction.getAmount() > 500).collect(Collectors.toList());System.out.println(transactionsMoreThan500);

Вывод программы:

[Transaction(id=3, amount=550.0, accountNumber=A001),Transaction(id=9, amount=3040.0, accountNumber=A003)]

2.2 Использование mapMulti()

Метод mapMulti() позволяет применять условия к элементам потока и выводить элемент по нашему выбору, если мы этого хотим. Мы можем пропустить выходной элемент или даже вернуть несколько элементов для одного входного элемента.

Мы можем использовать метод mapMulti() для добавления условия(сумма больше 500) и возврата таких отфильтрованных транзакций следующим образом:

List<Transaction> transactionsMoreThan500_V2 = accStmts.stream().<Transaction>mapMulti((stmt, consumer) -> {for(Transaction t : stmt.getTransactions()) {if(t.getAmount() > 500) {consumer.accept(t);}}}).collect(Collectors.toList());System.out.println(transactionsMoreThan500_V2);

Вывод программы:

[Transaction(id=3, amount=550.0, accountNumber=A001),Transaction(id=9, amount=3040.0, accountNumber=A003)]

3. Получение списка выписок по счетам

Иногда нам может понадобиться собрать счета, которые имеют транзакции на сумму более 500. В таком случае простая операция flatmap() не поможет. Давайте посмотрим, как мы можем выполнить фильтрацию в этом случае.

3.1 Использование mapMulti()

В этом решении мы внесли небольшое изменение в предыдущее решение mapMulti(). Все то же самое, пока мы не проверим условие(сумма > 500). В этом решении вместо добавления экземпляра транзакции мы добавляем сам оператор в нижестоящего потребителя.

List<AccountStatement> stmtHavingTransactionsMoreThan500 = accStmts.stream().<AccountStatement>mapMulti((stmt, consumer) -> {for(Transaction t : stmt.getTransactions()) {if(t.getAmount() > 500) {consumer.accept(stmt); // Slight change here - Add 'stmt' in place of 't'break;}}})filter.collect(Collectors.toList());System.out.println(stmtHavingTransactionsMoreThan500);

Вывод программы:

[AccountStatement(id=1, accountNumber=A001, transactions=[...]),AccountStatement(id=3, accountNumber=A003, transactions=[...])]

3.2 Использование filter() и anyMatch()

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

List<AccountStatement> stmtHavingTransactionsMoreThan500_V2 = accStmts.stream().filter(stmt -> stmt.getTransactions().stream().anyMatch(transaction -> transaction.getAmount() > 500)).collect(Collectors.toList());System.out.println(stmtHavingTransactionsMoreThan500_V2);

Вывод программы:

[AccountStatement(id=1, accountNumber=A001, transactions=[...]),AccountStatement(id=3, accountNumber=A003, transactions=[...])]

4. Заключение

В этом кратком руководстве по Java обсуждались различные подходы к фильтрации потока Java на основе условия вложенной коллекции. Мы рассмотрели решения с использованием методов flatMap(), map(), mapMulti() и anyMatch(), предоставляемых интерфейсом Stream.

Исходный код на Github

Прокрутить вверх