流过滤以获得最佳匹配

use*_*611 16 java java-8 java-stream

我的目标是过滤最佳匹配.在我的例子中,我有一个人员列表,我想按姓氏和名字过滤.

匹配的优势将是:

  1. 姓氏和名字匹配,返回第一场比赛
  2. 只有姓氏匹配,返回第一场比赛
  3. 没有比赛,抛出一些例外

我的代码到目前为止:

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")).???
Run Code Online (Sandbox Code Playgroud)

Cyr*_*ril 16

假设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 ..."))
    );
Run Code Online (Sandbox Code Playgroud)

  • @Eugene:如果有完全匹配,这个解决方案已经是最小的.如果不是,则至少处理一次所有元素是不可避免的.根据匹配的可能性,您可以专注于避免迭代元素两次. (3认同)

Hol*_*ger 7

您可以使用

Person person = persons.stream()
        .filter(p -> p.getSurName().equals("Doe"))
        .max(Comparator.comparing(p -> p.getFirstName().equals("Jane")))
        .orElse(null);
Run Code Online (Sandbox Code Playgroud)

它只会考虑具有正确姓氏的元素并返回它们的最佳元素,即具有匹配名字的元素.否则,返回第一个匹配元素.

正如评论中已经提到的,for如果存在最佳元素,则循环可以更有效,因为它可以短路.如果没有匹配姓氏和名字的最佳元素,则必须在所有实现中检查所有元素.

  • 这是一种有趣的方法.稳定性怎么样?如果有两个或更多人使用'Doe`姓氏而且他们都没有被命名为`Jane`,那么这总是会返回相同的`Person`吗? (3认同)
  • @FedericoPeraltaSchaffner:这将返回,第一个完整的比赛,如果有至少一个或第一人称用,如果没有完整的匹配匹配的姓.当然,这种行为是有点得以确认,也请参阅[这个答案](/sf/answers/2570124441/)... (3认同)
  • @FedericoPeraltaSchaffner:注意这个比较器只比较`Boolean`值; 所有具有匹配名字的人被认为彼此相等,并且所有没有匹配名字的人被认为彼此相等.因此,如果没有名字匹配,将返回第一个"相等"的姓氏匹配. (3认同)