Найти элемент
Теперь мы знакомы с этим элегантным методом трансформации коллекций, но для поиска элементов он бесполезен. Но для этого и родился метод фильтра.
Теперь мы хотим удалить имена, начинающиеся с N, из списка имен. Конечно, их может не быть, и результатом может быть пустое множество. Давайте сначала реализуем это, используя старый метод.
Скопируйте код кода следующим образом:
окончательный список<String> начинается сN = новый ArrayList<String>();
for(Имя строки: друзья) {
if(name.startsWith("N")) {
startWithN.add(имя);
}
}
Написание такого большого количества кода для такого простого события является довольно многословным занятием. Сначала мы создаем переменную, а затем инициализируем ее пустой коллекцией. Затем просмотрите исходную коллекцию и найдите имена, начинающиеся с указанной буквы. Если он найден, он вставляется в коллекцию.
Давайте воспользуемся методом фильтра, чтобы восстановить приведенный выше код и увидеть, насколько он мощный.
Скопируйте код кода следующим образом:
окончательный список<String> начинается сN =
друзья.поток()
.filter(имя -> name.startsWith("N"))
.collect(Коллекторы.toList());
Метод фильтра получает лямбда-выражение, которое возвращает логическое значение. Если выражение имеет значение true, этот элемент в контексте выполнения добавляется в набор результатов, в противном случае он пропускается. В конечном итоге возвращается Steam, который содержит только те элементы, выражение которых возвращает true. Наконец, мы используем метод сбора для преобразования коллекции в список — мы обсудим этот метод более подробно в разделе «Использование метода сбора и класса Collecters» на странице 52.
Давайте напечатаем элементы в этом наборе результатов:
Скопируйте код кода следующим образом:
System.out.println(String.format("Найдено %d имен", startWithN.size()));
Из выходных данных очевидно, что этот метод нашел все соответствующие элементы в коллекции.
Скопируйте код кода следующим образом:
Найдено 2 имени
Метод фильтра, как и метод карты, также возвращает итератор, но это все. Коллекция, возвращаемая картой, имеет тот же размер, что и входная коллекция, но трудно сказать, какой фильтр возвращает. Диапазон размеров возвращаемого набора: от 0 до количества элементов во входном наборе. В отличие от карты, фильтр возвращает подмножество входного набора.
Пока что мы очень довольны простотой кода, которую обеспечивают лямбда-выражения, но если мы не будем осторожны, проблема избыточности кода начнет медленно нарастать. Давайте обсудим этот вопрос ниже.
Повторное использование лямбда-выражений
Лямбда-выражения выглядят очень лаконично, но на самом деле код легко сделать избыточным, если не соблюдать осторожность. Избыточность приведет к низкому качеству кода и сложности в сопровождении, если мы хотим внести изменения, нам придется изменить несколько связанных кодов вместе.
Избежание избыточности также может помочь нам улучшить производительность. Соответствующий код сосредоточен в одном месте, чтобы мы могли проанализировать его производительность, а затем оптимизировать код здесь, что может легко улучшить производительность кода.
Теперь давайте разберемся, почему использование лямбда-выражений может легко привести к избыточности кода, и подумаем, как этого избежать.
Скопируйте код кода следующим образом:
окончательный список<String> друзей =
Arrays.asList("Брайан", "Нейт", "Нил", "Раджу", "Сара", "Скотт");
окончательные редакторы List<String> =
Arrays.asList("Брайан", "Джеки", "Джон", "Майк");
окончательный список<String> товарищи =
Arrays.asList("Кейт", "Кен", "Ник", "Паула", "Зак");
Мы хотим отфильтровать имена, начинающиеся с определенной буквы.
Мы хотим фильтровать имена, начинающиеся с определенной буквы. Давайте сначала просто реализуем это, используя метод фильтра.
Скопируйте код кода следующим образом:
окончательный длинный счетчикFriendsStartN =
друзья.поток()
.filter(name -> name.startsWith("N")).count();
окончательный длинный счетчикEditorsStartN =
редакторы.stream()
.filter(name -> name.startsWith("N")).count();
окончательный длинный счетчикComradesStartN =
товарищи.поток()
.filter(name -> name.startsWith("N")).count();
Лямбда-выражения делают код кратким, но неосознанно вносят в него избыточность. В приведенном выше примере, если мы хотим изменить лямбда-выражение, нам придется изменить более одного места, что невозможно. К счастью, мы можем присваивать переменным лямбда-выражения и повторно использовать их как объекты.
Метод фильтра, получатель лямбда-выражения, получает ссылку на функциональный интерфейс java.util.function.Predicate. Здесь снова пригодится компилятор Java. Он генерирует реализацию тестового метода Predicate, используя указанное лямбда-выражение. Теперь мы можем более явно попросить компилятор Java сгенерировать этот метод вместо того, чтобы генерировать его там, где определены параметры. В приведенном выше примере мы можем явно сохранить лямбда-выражение в ссылке типа Predicate, а затем передать эту ссылку методу фильтра, что позволяет легко избежать избыточности кода;
Давайте проведем рефакторинг предыдущего кода, чтобы он соответствовал принципу DRY. (Принцип «Не повторяйте свое» — DRY — см. в книге «Программист-прагматик: от подмастерья к мастеру» [HT00]).
Скопируйте код кода следующим образом:
Final Predicate<String> startWithN = name -> name.startsWith("N");
окончательный длинный счетчикFriendsStartN =
друзья.поток()
.filter(startWithN)
.считать();
окончательный длинный счетчикEditorsStartN =
редакторы.stream()
.filter(startWithN)
.считать();
окончательный длинный счетчикComradesStartN =
товарищи.stream()
.filter(startWithN)
.считать();
Теперь вместо того, чтобы снова писать лямбда-выражение, мы пишем его один раз и сохраняем в ссылке типа Predicate с именем startWithN. В следующих трех вызовах фильтра компилятор Java увидел лямбда-выражение, замаскированное под предикат, улыбнулся и молча принял его.
Эта недавно введенная переменная устраняет для нас избыточность кода. Но к сожалению, как мы увидим позже, враг скоро вернется, чтобы отомстить. Посмотрим, какое более мощное оружие сможет уничтожить его за нас.