Фильтр потока для наилучшего соответствия

Моя цель - фильтр для лучшего соответствия. В моем примере у меня есть список лиц, которые я хочу фильтровать по фамилии и имени.

Соответствующая превенция будет:

  • как фамилия, так и имя, совпадение первого имени
  • совпадение только фамилии, возвращение первого совпадения
  • нет совпадения, исключение исключений

Мой код:

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