Перейти к основному содержимому

Как отфильтровать коллекцию в Java

· 5 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

1. Обзор

В этом кратком руководстве мы рассмотрим различные способы фильтрации коллекции в Java , то есть поиск всех элементов, соответствующих определенному условию.

Это фундаментальная задача, которая присутствует практически в любом Java-приложении.

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

В частности, в этом уроке мы рассмотрим:

  • Функция фильтра Java 8 Streams ()
  • Фильтрующий сборщик Java 9
  • Соответствующие API коллекций Eclipse
  • Метод Apache CollectionUtils filter()
  • Подход Guava Collections2 filter()

2. Использование потоков

С момента появления Java 8 потоки стали играть ключевую роль в большинстве случаев, когда нам приходится обрабатывать набор данных.

Следовательно, в большинстве случаев это предпочтительный подход, поскольку он построен на Java и не требует дополнительных зависимостей.

2.1. Фильтрация коллекции с потоками

Для простоты во всех примерах нашей целью будет создание метода, который извлекает только четные числа из набора значений Integer .

Таким образом, мы можем выразить условие, которое мы будем использовать для оценки каждого элемента, как « значение % 2 == 0 ».

Во всех случаях нам нужно будет определить это условие как объект Predicate :

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
Predicate<Integer> streamsPredicate = item -> item % 2 == 0;

return baseCollection.stream()
.filter(streamsPredicate)
.collect(Collectors.toList());
}

Важно отметить, что каждая библиотека, которую мы анализируем в этом руководстве, предоставляет свою собственную реализацию Predicate , но при этом все они определены как функциональные интерфейсы, что позволяет нам использовать функции Lambda для их объявления.

В этом случае мы использовали предопределенный Collector , предоставленный Java, который накапливает элементы в List , но мы могли бы использовать и другие, как обсуждалось в этом предыдущем посте .

2.2. Фильтрация после группировки коллекции в Java 9

Потоки позволяют нам объединять элементы с помощью сборщика groupingBy .

Тем не менее, если мы фильтруем, как мы это делали в предыдущем разделе, некоторые элементы могут быть отброшены на ранней стадии, до того, как этот сборщик вступит в игру.

По этой причине фильтрующий коллектор был представлен в Java 9 с целью обработки вложенных коллекций после их группировки. ``

Следуя нашему примеру, давайте представим, что мы хотим сгруппировать нашу коллекцию на основе количества цифр, которое имеет каждое целое число, прежде чем отфильтровать нечетные числа:

public Map<Integer, List<Integer>> findEvenNumbersAfterGrouping(
Collection<Integer> baseCollection) {

Function<Integer, Integer> getQuantityOfDigits = item -> (int) Math.log10(item) + 1;

return baseCollection.stream()
.collect(groupingBy(
getQuantityOfDigits,
filtering(item -> item % 2 == 0, toList())));
}

Короче говоря, если мы используем этот сборщик, мы можем получить пустую запись значения, тогда как если мы фильтруем перед группировкой, сборщик вообще не создаст такую запись.

Конечно, мы бы выбрали подход, исходя из наших требований.

3. Использование коллекций Eclipse

Мы также можем использовать некоторые другие сторонние библиотеки для достижения нашей цели, либо потому, что наше приложение не поддерживает Java 8, либо потому, что мы хотим воспользоваться некоторыми мощными функциями, не предоставляемыми Java.

Так обстоит дело с Eclipse Collections , библиотекой, которая стремится идти в ногу с новыми парадигмами, развиваясь и принимая изменения, внесенные во все последние версии Java.

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

3.1. Зависимости

Начнем с добавления следующей зависимости в pom.xml нашего проекта :

<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>9.2.0</version>
</dependency>

Коллекция eclipse включает в себя все необходимые интерфейсы структур данных и сам API.

3.2. Фильтрация коллекции с помощью коллекций Eclipse

Давайте теперь воспользуемся функцией фильтрации eclipse для одной из его структур данных, например MutableList :

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
Predicate<Integer> eclipsePredicate
= item -> item % 2 == 0;

Collection<Integer> filteredList = Lists.mutable
.ofAll(baseCollection)
.select(eclipsePredicate);

return filteredList;
}

В качестве альтернативы мы могли бы использовать статический метод select() класса Iterate для определения объекта filteredList : ``

Collection<Integer> filteredList
= Iterate.select(baseCollection, eclipsePredicate);

4. Использование Apache CollectionUtils

Чтобы начать работу с библиотекой Apache CollectionUtils , мы можем ознакомиться с этим кратким руководством , в котором мы рассмотрели ее использование.

Однако в этом руководстве мы сосредоточимся на его реализации filter() .

4.1. Зависимости

Во-первых, нам понадобятся следующие зависимости в нашем файле pom.xml :

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.2</version>
</dependency>

4.2. Фильтрация коллекции с помощью CollectionUtils

Теперь мы готовы использовать методы CollectonUtils :

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
Predicate<Integer> apachePredicate = item -> item % 2 == 0;

CollectionUtils.filter(baseCollection, apachePredicate);
return baseCollection;
}

Мы должны учитывать, что этот метод модифицирует baseCollection , удаляя каждый элемент, не соответствующий условию.

Это означает, что базовая коллекция должна быть изменчивой, иначе будет выдано исключение .

5. Использование коллекций Guava2

Как и прежде, мы можем прочитать наш предыдущий пост «Фильтрация и преобразование коллекций в Guava» для получения дополнительной информации по этому вопросу.

5.1. Зависимости

Давайте начнем с добавления этой зависимости в наш файл pom.xml :

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>

5.2. Фильтрация коллекции с помощью Collections2

Как мы видим, этот подход очень похож на тот, который использовался в предыдущем разделе:

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
Predicate<Integer> guavaPredicate = item -> item % 2 == 0;

return Collections2.filter(baseCollection, guavaPredicate);
}

Опять же, здесь мы определяем объект Predicate , специфичный для Guava .

В этом случае Guava не изменяет baseCollection , а создает новую, поэтому мы можем использовать неизменяемую коллекцию в качестве входных данных.

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

Подводя итог, мы увидели, что существует множество различных способов фильтрации коллекций в Java.

Несмотря на то, что потоки обычно являются предпочтительным подходом, полезно знать и помнить о функциях, предлагаемых другими библиотеками.

Особенно, если нам нужно поддерживать старые версии Java. Однако, если это так, нам нужно помнить о последних функциях Java, используемых в этом руководстве, таких как лямбда-выражения, которые следует заменить анонимными классами.

Как обычно, мы можем найти все примеры, показанные в этом руководстве, в нашем репозитории Github .