用于过滤对象的Java lambda函数

用于过滤对象的Java lambda函数,java,java-stream,Java,Java Stream,我有几个person对象和一个为给定实体id选择person的函数: person1 = { name = "abc"; id = "1"; } person2 = { name = "xyz"; id = "2"; } person3 = { name = "aaa"; id = null; } Person getPersonFunction(List<Person> persons, String id) { //Get

我有几个person对象和一个为给定实体id选择person的函数:

person1 = {
    name = "abc";
    id = "1";
}
person2 = {
    name = "xyz";
    id = "2";
}
person3 = {
    name = "aaa";
    id = null;
}

Person getPersonFunction(List<Person> persons, String id) {
   //Get the person for the given id and if id is not for any person from the list then select the person with null as id.
}
person1={
name=“abc”;
id=“1”;
}
人员2={
name=“xyz”;
id=“2”;
}
人员3={
name=“aaa”;
id=null;
}
Person getPersonFunction(列出人员,字符串id){
//获取给定id的人员,如果id不适用于列表中的任何人员,则选择id为null的人员。
}
问题是:我想为给定的实体id选择一个人,如果给定实体id的列表中没有这样的人,那么选择实体id设置为null的人。这些人总是一个人,其实体id设置为null,就像一个默认的人一样。 我可以使用for循环轻松地解决这个问题,但我想使用java流解决这个问题。 以下是我所做的:

Map<String, Person> personMap = persons.stream().collect(Collectors.toMap(Person::id, person -> person);

Person person = personMap.getOrDefault(id, personMap.get(null))
Map personMap=persons.stream().collect(Collectors.toMap(Person::id,Person->Person);
Person-Person=personMap.getOrDefault(id,personMap.get(null))

这给了我正确的结果,但我想在一行中完成它。

如果找不到人,您可以再次流式处理它(这显然不是最佳的)


另一种选择是使用有状态映射来存储(并记住)Person将
null
id(同样听起来不太好)

我建议避免尝试使用流API消除行;这不是它的目的

您处理收集和构建地图的想法很好。您还可以重用地图,如果您试图将其强制为一行,这是不可能的。此外,构建地图与使用地图拉住满足您约束的人之间的明显区别是自我记录(这使人们更容易理解您的代码)

多考虑可读性和算法设计,少考虑如何用最少的行数完成任务


当然,这并不是说您不应该减少代码重复。

做到这一点的清晰方法是

Person NOBODY = new Person("NOBODY", null);

private Person findPerson(List<Person> people, String id) {
    return people.stream()
            // Anyone with the right or null id.
            .filter(p -> p.id == null || p.id.equals(id))
            // Sort putting null ids at the end.
            .sorted(Comparator.comparing(p -> p.id == null))
            // Take first.
            .findFirst()
            // Should never happen.
            .orElse(NOBODY);
}
Person NOBODY=新人(“NOBODY”,空);
私人findPerson(列出人员,字符串id){
returnpeople.stream()
//具有正确或空id的任何人。
.filter(p->p.id==null | | p.id.equals(id))
//排序将空ID放在末尾。
.sorted(Comparator.comparing(p->p.id==null))
//先走一步。
.findFirst()
//不应该发生。
.orElse(无人);
}

由于没有为错误案例定义行为,因此可以使用

Person getPersonFunction(List<Person> persons, String id) {
   return persons.stream()
        .max(Comparator.comparingInt(p -> id.equals(p.id())? 1: p.id()==null? 0: -1))
        .get(); // throws NoSuchElementException if list is empty
}

这更有效,因为当找到匹配项时,它会立即返回,同时仍然能够在不进行其他搜索操作的情况下提供回退。

为什么它需要在一行上?删除换行符,然后再看一行!如果搜索单个
id
,为什么要构建地图?只需线性搜索。你确定吗e列表是一个首先要使用的正确数据结构吗?如果ID是唯一的,那么最好在整个过程中使用一个映射。@rustyx他需要它来优化他的搜索。首先他将在
O(n)
步骤中搜索整个列表。然后如果他找不到它的元素,他将使用映射进行O(1)类型的搜索使用
id==null
(a,b)->a.id==null和&b.id==null找到元素的步骤?0:a.id==null?1:-1
您仍然认为这是一种明确的方法吗?:)比较器破坏了接口的契约。它在实践中可能很好地工作(我还没有测试),但使用比较器.nullslat()等方法更安全要正确地解决这个问题。@cpp初学者很容易解决:
(a,b)->(a.id==null)=(b.id==null)?0:a.id==null?1:-1
,但我也会使用工厂方法。在这种特定情况下,
.sorted(Comparator.comparating(p->p.id==null))
比处理
更简单
必须与另一个属性比较器组合(
.sorted(comparator.comparing(p->p.id,comparator.nullsLast(null)))
)…顺便说一句,问题是关于搜索一个ID,而只搜索一个ID。不涉及姓名搜索。@Holger-感谢您的帮助-第一次使用
nullsLast
,以前从未见过它。
Person getPersonFunction(List<Person> persons, String id) {
   return persons.stream()
        .max(Comparator.comparingInt(p -> id.equals(p.id())? 1: p.id()==null? 0: -1))
        .get(); // throws NoSuchElementException if list is empty
}
Person getPersonFunction(List<Person> persons, String id) {
    Person nullId = null;
    for(Person p: persons)
        if(id.equals(p.id())) return p; else if(p.id()==null) nullId = p;
    return Objects.requireNonNull(nullId, "no person with null id in list");
}