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

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

1. обзор

В этом коротком руководствеwe’ll have a look at different ways of filtering a Collection in Java - то есть поиск всех элементов, соответствующих определенному условию.

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

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

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

  • Функцияfilter() Java 8 Streams

  • Сборщик Java 9filtering

  • Соответствующие APIEclipse Collections

  • Метод ApacheCollectionUtils filter()

  • Подход ГуавыCollections2 filter()

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

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

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

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

Для простотыin all the examples our objective will be to create a method that retrieves only the even numbers from a Collection значенийInteger.

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

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

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

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

Важно отметить, чтоeach library we analyze in this tutorial provides its own Predicate implementation, но все же все они определены как функциональные интерфейсы, что позволяет нам использовать лямбда-функции для их объявления.

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

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

Потоки позволяют нам агрегировать элементы, используяgroupingBy collector.

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

По этой причинеthe filtering collector was introduced with Java 9, with the objective of processing the subcollections after they have been grouped.

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

public Map> findEvenNumbersAfterGrouping(
  Collection baseCollection) {

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

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

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

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

3. ИспользуяEclipse Collections

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

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

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

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

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


    org.eclipse.collections
    eclipse-collections
    9.2.0

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

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

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

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

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

    return filteredList;
}

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

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

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

Чтобы начать работу с библиотекой ApacheCollectionUtils, мы можем проверитьthis short tutorial, где мы рассмотрели ее использование.

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

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

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


    org.apache.commons
    commons-collections4
    4.2

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

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

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

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

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

Это означает, чтоthe base Collection has to be mutable, otherwise it will throw an exception.

5. ИспользованиеCollections2 Guava

Как и раньше, мы можем прочитать наш предыдущий пост‘Filtering and Transforming Collections in Guava' для получения дополнительной информации по этому вопросу.

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

Начнем с добавленияthis dependency в наш файлpom.xml:


    com.google.guava
    guava
    25.1-jre

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

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

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

    return Collections2.filter(baseCollection, guavaPredicate);
}

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

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

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

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

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

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

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