«1. Введение

В этом кратком руководстве мы рассмотрим использование метода Stream.filter() при работе с потоками в Java.

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

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

Метод filter() является промежуточной операцией интерфейса Stream, которая позволяет нам фильтровать элементы потока, соответствующие заданному предикату:

Stream<T> filter(Predicate<? super T> predicate)

Чтобы увидеть как это работает, давайте создадим класс Customer:

public class Customer {
    private String name;
    private int points;
    //Constructor and standard getters
}

Кроме того, давайте создадим коллекцию клиентов:

Customer john = new Customer("John P.", 15);
Customer sarah = new Customer("Sarah M.", 200);
Customer charles = new Customer("Charles B.", 150);
Customer mary = new Customer("Mary T.", 1);

List<Customer> customers = Arrays.asList(john, sarah, charles, mary);

2.1. Фильтрация коллекций

Распространенным вариантом использования метода filter() является обработка коллекций.

Давайте составим список клиентов с более чем 100 баллами. Для этого мы можем использовать лямбда-выражение:

List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100)
  .collect(Collectors.toList());

Мы также можем использовать ссылку на метод, которая является сокращением для лямбда-выражения:

List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(Customer::hasOverHundredPoints)
  .collect(Collectors.toList());

В этом случае мы добавили метод hasOverHundredPoints в наш Класс клиента:

public boolean hasOverHundredPoints() {
    return this.points > 100;
}

В обоих случаях мы получаем один и тот же результат:

assertThat(customersWithMoreThan100Points).hasSize(2);
assertThat(customersWithMoreThan100Points).contains(sarah, charles);

2.2. Фильтрация коллекций с несколькими критериями

Кроме того, мы можем использовать несколько условий с filter(). Например, мы можем фильтровать по точкам и имени:

List<Customer> charlesWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100 && c.getName().startsWith("Charles"))
  .collect(Collectors.toList());

assertThat(charlesWithMoreThan100Points).hasSize(1);
assertThat(charlesWithMoreThan100Points).contains(charles);

3. Обработка исключений

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

Далее мы покажем несколько различных способов обработки исключений в лямбда-выражениях.

3.1. Использование пользовательской оболочки

Во-первых, мы начнем с добавления profilePhotoUrl к нашему клиенту:

private String profilePhotoUrl;

Кроме того, давайте добавим простой метод hasValidProfilePhoto() для проверки доступности профиля:

public boolean hasValidProfilePhoto() throws IOException {
    URL url = new URL(this.profilePhotoUrl);
    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
}

~ ~~ Мы видим, что метод hasValidProfilePhoto() генерирует исключение IOException. Теперь, если мы попытаемся отфильтровать клиентов с помощью этого метода:

List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(Customer::hasValidProfilePhoto)
  .collect(Collectors.toList());

Мы увидим следующую ошибку: блок catch:

Incompatible thrown types java.io.IOException in functional expression

Если нам нужно сгенерировать исключение из нашего предиката, мы можем обернуть его в непроверенное исключение, такое как RuntimeException.

List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(c -> {
      try {
          return c.hasValidProfilePhoto();
      } catch (IOException e) {
          //handle exception
      }
      return false;
  })
  .collect(Collectors.toList());

3.2. Использование ThrowingFunction

В качестве альтернативы мы можем использовать библиотеку ThrowingFunction.

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

Давайте начнем с добавления функции throwing в наш pom:

Для обработки исключений в предикатах эта библиотека предлагает нам класс ThrowingPredicate, который имеет метод unchecked() для переноса проверенных исключений.

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

Давайте посмотрим на это в действии:

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

List customersWithValidProfilePhoto = customers
  .stream()
  .filter(ThrowingPredicate.unchecked(Customer::hasValidProfilePhoto))
  .collect(Collectors.toList());

В этой статье мы рассмотрели пример использования метода filter() для обработки потоков. Мы также изучили некоторые альтернативы для обработки исключений.

Как всегда, полный код доступен на GitHub.

«