Фильтр потока для наилучшего соответствия
Моя цель - фильтр для лучшего соответствия. В моем примере у меня есть список лиц, которые я хочу фильтровать по фамилии и имени.
Соответствующая превенция будет:
- как фамилия, так и имя, совпадение первого имени
- совпадение только фамилии, возвращение первого совпадения
- нет совпадения, исключение исключений
Мой код:
final List<Person> persons = Arrays.asList(
new Person("Doe", "John"),
new Person("Doe", "Jane"),
new Person("Munster", "Herman");
Person person = persons.stream().filter(p -> p.getSurname().equals("Doe")).???
Ответы
Ответ 1
Предполагая, что Person реализует equals и hashCode:
Person personToFind = new Person("Doe", "Jane");
Person person = persons.stream()
.filter(p -> p.equals(personToFind))
.findFirst()
.orElseGet(() ->
persons.stream()
.filter(p -> p.getSurname().equals(personToFind.getSurname()))
.findFirst()
.orElseThrow(() -> new RuntimeException("Could not find person ..."))
);
Ответ 2
Вы можете использовать
Person person = persons.stream()
.filter(p -> p.getSurName().equals("Doe"))
.max(Comparator.comparing(p -> p.getFirstName().equals("Jane")))
.orElse(null);
Он рассмотрит только элементы, имеющие правильную фамилию, и вернет им лучший элемент, который совпадает с совпадающим именем. В противном случае возвращается первый соответствующий элемент.
Как уже упомянутый в комментарии, цикл for
может быть более эффективным, если есть лучший элемент, так как он может коротко замыкаться. Если нет лучшего элемента с совпадающими фамилиями и именем, все элементы должны быть проверены во всех реализациях.
Ответ 3
Я бы предложил следующее:
Optional<Person> bestMatch = persons.stream()
.filter(p -> "Doe".equals(p.getSurname()))
.reduce((person, person2) -> {
if ("John".equals(person.getFirstName())) {
return person;
} else if ("John".equals(person2.getFirstName())) {
return person2;
}
return person;
});
Person result = bestMatch.orElseThrow(IllegalArgumentException::new);
Ответ 4
Правильный инструмент .findFirst()
. Вы также можете использовать .limit(1)
.