Java 8 Lambda 表达式获取 Optional<List> 的第一个元素

16

我是新手,在尝试使用Java 8的流和Lambda。有人能帮我处理以下代码吗?需要找到列表中第一个可选人的年龄。这个列表可能为空。

代码中缺少对列表第一个元素的空值检查。我也想要这个检查。

需要类似于以下代码:

//input parameter 
//Optional<List<Person>> persons
public Integer getAgeOfFirstPerson(Optional<List<Person>> persons) {
    if (!persons.isPresent()) {
        return null;
    }

    Integer age = persons.get()
        .get(0)
        .getAge();

    return age;
}

//Person.java
class Person {
    Integer age;
    //getters and setters
}

7
不要使用 Optional 作为参数。 - Naman
谢谢留言 :) - meso
1
或许有争议,但你最好避免使用 Optional<List<?>> ,就像避免使用 nullList 变量一样:空的 Optional<List<?>> 和包含空 List 的非空 Optional<List<?>> 之间真的有区别吗?如果没有,请不要使用 Optional ,而是使用空的 List 。如果有区别,则可能需要定义自己的类型来清楚地描述它。 - Didier L
@DidierL 我不同意“如果是”的部分:Optional<List<?>>已经非常清楚地描述了它。另一方面,我从未被引用在 https://dev59.com/rmAf5IYBdhLWcg3wizEE#24564612 中的论点所说服 :) - Alexey Romanov
@AlexeyRomanov,“Optional<List<?>>”是什么意思?缺少列表和空列表之间是否有语义差异? - Holger
3个回答

30
假设你的Optional里面的List可能为空,那么你也应该考虑这种情况。最简单的方法是:
return persons.flatMap(list -> list.stream().findFirst()).map(Person::getAge).orElse(null);

不幸的是,这个方法不能处理第一个列表项为null的情况,因为当Stream的第一个元素为null时,findFirst()会抛出NullPointerException(正如Holger在评论中所指出的)。

因此,如果您想跳过null列表项并获取第一个非空项,请按以下方式操作:

return persons.flatMap(list -> list.stream().filter(Objects::nonNull).findFirst())
              .map(Person::getAge)
              .orElse(null);
否则,如果您总是想获取第一个条目,即使它为null,您可以像这样进行调整:
return persons.flatMap(list -> list.stream().limit(1).filter(Objects::nonNull).findFirst())
              .map(Person::getAge)
              .orElse(null);

然而这变得相当复杂,因此更简单的方法是回到测试列表是否为空:

return persons.filter(list -> !list.isEmpty())
              .map(list -> list.get(0))
              .map(Person::getAge)
              .orElse(null);

@Holger,我忘记了。我认为在那里也建议避免在流中使用null项。无论如何,我已经相应地更新了答案。 - Didier L
2
是的,这可以推广到任何具有可变数量元素的数据结构,包括可能为零。这样的结构永远不应该是“null”,也永远不应该包含“null”,因为在大多数情况下,您可以通过没有这些元素甚至没有任何元素来表达相同的意思。因此,对于此问题的“List”参数,首先不应该是“Optional”,因为列表可以为空而不是不存在。 - Holger
@Holger,我非常同意你的观点。 - Didier L

1
您可以使用 Optional.map
public int getAgeOfFirstPerson(Optional<List<Person>> persons){ 
    return persons.map(people -> people.get(0).getAge()).orElse(0);
}

请注意,原始数据类型没有null(它们不是对象),因此用0替换了它。
或者根据由@Alexey回答进一步改进,如果需要检查人是否为空。

1
如果返回类型是int,则return null将无法编译。假设您想要一个缺失的Optional<Integer>。保留HTML标记。
return persons.map(list -> list.get(0)).map(Person::getAge)

这将起作用(不幸的是,没有像流一样返回OptionalInt的mapToInt)。需要两个map来处理。

代码中缺少对列表第一个元素的空值检查。也希望有这个检查。


编辑了代码。我更喜欢返回整数。 - meso
然后只需在末尾添加 .orElse(null)。但是,正如DidierL指出的那样,这会忽略“列表为空”的情况。 - Alexey Romanov
1
如果列表为空,list -> list.get(0) 将抛出一个索引越界异常 :( - shakeel
@shakeel请查看Didier的答案中的最新版本。 - Alexey Romanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接