简体   繁体   English

在 Java 8 中将两行代码初始化和填充列表合并为单个语句?

[英]Combine the Two Lines of code initializing and populating a List into a Single Statement in Java 8?

I have a list of user id pmUserIds .我有一个用户 ID pmUserIds的列表。 For each id in the list I'm making a call getUserByUserId() and placing the result in a list of type List<Optional<User>> .对于列表中的每个id ,我都会调用getUserByUserId()并将结果放入List<Optional<User>>类型的列表中。

I want to combine these 2 lines into a single statement:我想将这两行合并为一个语句:

List<Optional<User>> pmUsers = new ArrayList<>();

pmUserIds.forEach(userId -> pmUsers.add(userRepository.getUserByUserId(userId)));

Something like that wouldn't work:像这样的东西是行不通的:

List<Optional<User>> pmUsers  = pmUserIds.forEach(userId -> pmUsers.add(userRepository.getUserByUserId(userId)));

It will say that pmUsers not have been initiated.它会说pmUsers尚未启动。 Is there any way we could do this in single line?有什么办法可以在单行中做到这一点?

Use Stream.map instead of forEach , then convert to List with collect :使用Stream.map代替forEach ,然后使用collect转换为List

List<Optional<User>> pmUsers = pmUserIds.stream()
        .map(userId -> userRepository.getUserByUserId(userId))
        .collect(Collectors.toList());

That's still not one line (except you want a really long line), but it's somewhat clearer what the code does that with separate initialization and forEach .这仍然不是一行(除非您想要很长的一行),但是代码通过单独的初始化和forEach做了什么更清楚了。 You may also use a method reference instead of that lambda, ie .map(userRepository::getUserByUserId)您也可以使用方法参考而不是 lambda,即.map(userRepository::getUserByUserId)

Your second attempt is not viable because method forEach() is void .您的第二次尝试不可行,因为方法forEach()void To do the job in a single statement, you need a Stream .要在单个语句中完成这项工作,您需要一个Stream

But there is more to it.但还有更多。

Storing Optional objects into a Collection is an antipattern .Optional对象存储到Collection中是一种反模式 It almost the same as storing null references because optional not necessarily contains a value.它与存储null引用几乎相同,因为可选不一定包含值。

Both null and empty optional are meant to represent the absence of data and nothing else. null空选项都表示没有数据,仅此而已。 If one of them has a separate meaning in your application (apart from "no value" ), it's a smell.如果其中一个在您的应用程序中具有单独的含义(除了"no value" ),那就是一种气味。

If you need to fire a particular action when a call getUserByUserId() returns an empty optional , then do it right after receiving the optional instead of placing it to the list (and by the way, Stream is not the right tool when you need to perform some side-effects along the way, it would be better to stick with plain loops).如果您需要在调用getUserByUserId()返回一个空的 optional时触发特定操作,那么在接收到optional后立即执行此操作,而不是将其放入列表中(顺便说一下,Stream 不是您需要时的正确工具在此过程中执行一些副作用,最好坚持使用普通循环)。

Here's a small quote from the answer by @Stuart Marks , Java and OpenJDK developer:这是@Stuart Marks 、Java 和 OpenJDK 开发人员的回答中的一小段引述:

I'm sure somebody could come up with some contrived cases where they really want to store an Optional in a field or a collection , but in general, it is best to avoid doing this .我敢肯定,有人可能会想出一些人为的情况,他们真的想将Optional存储fieldcollection中,但总的来说,最好避免这样做

I suggest you to check the presence of value in the Optional right on the spot (in the stream) and then "unpack" non-empty optional objects .我建议您当场(在流中)检查Optional右侧的值是否存在,然后“解包”非空可选对象

That's how it might look like:这就是它的样子:

List<User> pmUsers  = pmUserIds.stream()
    .map(userRepository::getUserByUserId) // Stream<Optional<User>>
    .filter(Optional::isPresent)
    .map(Optional::get)                   // Stream<User>
    .toList(); // for Java 16+ or .collect(Collectors.toList()) for earlier versions

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM