«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.
«