1. Обзор
Поиск элемента в списке — очень распространенная задача, с которой мы как разработчики сталкиваемся.
В этом кратком руководстве мы рассмотрим различные способы сделать это с помощью Java.
2. Настройка
Сначала давайте начнем с определения Customer
POJO:
public class Customer {
private int id;
private String name;
// getters/setters, custom hashcode/equals
}
Затем ArrayList
клиентов:
List<Customer> customers = new ArrayList<>();
customers.add(new Customer(1, "Jack"));
customers.add(new Customer(2, "James"));
customers.add(new Customer(3, "Kelly"));
Обратите внимание, что мы переопределили hashCode
и equals
в нашем классе Customer .
На основе нашей текущей реализации equals
два объекта Customer
с одинаковым идентификатором
будут считаться равными.
Мы будем использовать этот список клиентов
по пути.
3. Использование API Java
Сама Java предоставляет несколько способов поиска элемента в списке:
- Метод
содержит
_ - Метод
indexOf
_ - Специальный цикл for
- Потоковый API
_
3.1. содержит()
List
предоставляет метод с именем contains
:
boolean contains(Object element)
Как следует из названия, этот метод возвращает значение true
, если список содержит указанный элемент,
и значение false
в противном случае. ``
Поэтому, когда нам нужно проверить, существует ли конкретный элемент в нашем списке, мы можем:
Customer james = new Customer(2, "James");
if (customers.contains(james)) {
// ...
}
3.2. индекс чего-либо()
indexOf
— еще один полезный метод для поиска элементов:
int indexOf(Object element)
Этот метод возвращает индекс первого появления указанного элемента
в заданном списке или -1, если список не содержит элемент
.
Итак, логически, если этот метод возвращает что-либо, кроме -1, мы знаем, что список содержит элемент:
if(customers.indexOf(james) != -1) {
// ...
}
Основное преимущество использования этого метода заключается в том, что он может сообщить нам позицию указанного элемента в заданном списке.
3.3. Базовый цикл
А что, если мы хотим выполнить поиск элемента по полю? Например, скажем, мы объявляем лотерею, и нам нужно объявить Клиента
с определенным именем
победителем.
Для таких полевых поисков мы можем обратиться к итерации.
Традиционным способом перебора списка является использование одной из циклических конструкций Java. На каждой итерации мы сравниваем текущий элемент в списке с искомым элементом, чтобы увидеть, совпадают ли они:
public Customer findUsingEnhancedForLoop(
String name, List<Customer> customers) {
for (Customer customer : customers) {
if (customer.getName().equals(name)) {
return customer;
}
}
return null;
}
Здесь имя
относится к имени, которое мы ищем в данном списке клиентов
. Этот метод возвращает первый объект Customer
в списке с совпадающим именем
или null
, если такой Customer
не существует.
3.4. Зацикливание с помощью итератора
Итератор
— это еще один способ, с помощью которого мы можем перемещаться по списку элементов.
Мы можем просто взять наш предыдущий пример и немного изменить его:
public Customer findUsingIterator(
String name, List<Customer> customers) {
Iterator<Customer> iterator = customers.iterator();
while (iterator.hasNext()) {
Customer customer = iterator.next();
if (customer.getName().equals(name)) {
return customer;
}
}
return null;
}
Следовательно, поведение такое же, как и раньше.
3.5. API потока
Java 8
Начиная с Java 8, мы также можем использовать Stream
API для поиска элемента в списке.
Чтобы найти элемент, соответствующий определенным критериям в заданном списке, мы:
вызвать
stream()
в спискевызовите метод
filter
()
с правильнымпредикатом
вызвать
конструкцию findAny()
, которая возвращает первый элемент, соответствующий предикатуфильтра
, завернутый внеобязательный
элемент, если такой элемент существует. ****
Customer james = customers.stream()
.filter(customer -> "James".equals(customer.getName()))
.findAny()
.orElse(null);
Для удобства по умолчанию используется значение null
, если необязательный
параметр пуст, но это не всегда лучший выбор для каждого сценария.
4. Сторонние библиотеки
Теперь, когда Stream API более чем достаточно, что нам делать, если мы застряли на более ранней версии Java?
К счастью, существует множество сторонних библиотек, таких как Google Guava и Apache Commons, которые мы можем использовать.
4.1. Google Гуава
Google Guava предоставляет функциональность, похожую на то, что мы можем делать с потоками:
Customer james = Iterables.tryFind(customers,
new Predicate<Customer>() {
public boolean apply(Customer customer) {
return "James".equals(customer.getName());
}
}).orNull();
Как и в случае с Stream
API, мы можем при желании вернуть значение по умолчанию вместо null
:
Customer james = Iterables.tryFind(customers,
new Predicate<Customer>() {
public boolean apply(Customer customer) {
return "James".equals(customer.getName());
}
}).or(customers.get(0));
Приведенный выше код выберет первый элемент в списке, если совпадений не найдено.
Кроме того, не забывайте, что Guava генерирует исключение NullPointerException
, если список или предикат имеют значение null
.
4.2. Апач Коммонс
Мы можем найти элемент почти таким же образом, используя Apache Commons:
Customer james = IterableUtils.find(customers,
new Predicate<Customer>() {
public boolean evaluate(Customer customer) {
return "James".equals(customer.getName());
}
});
Однако есть пара важных отличий:
- Apache Commons просто возвращает
null
, если мы передаемнулевой
список. - Он `
не предоставляет функциональные возможности значений по умолчанию, такие как
tryFind в Guava.`
5. Вывод
В этой статье мы узнали о различных способах поиска элемента в списке,
начиная с быстрой проверки существования и заканчивая поиском по полю.
Мы также рассматривали сторонние библиотеки Google Guava
и Apache Commons
как альтернативу Java 8 Streams
API.
Спасибо, что заглянули, и не забудьте проверить все исходники этих примеров на GitHub.