从 Optional<> 转换为 ArrayList<>
Posted
技术标签:
【中文标题】从 Optional<> 转换为 ArrayList<>【英文标题】:Cast from Optional<> to ArrayList<> 【发布时间】:2015-10-19 17:19:16 【问题描述】:我有以下情况:
public ArrayList<A> getMethods()
return b.c.test();
所以,我的问题是b.c.test()
返回一个以Optional<A>
作为返回类型的值。但我需要返回一个ArrayList<A>
。
所以,我尝试将其转换为:
public ArrayList<A> getMethods()
return (ArrayList<A>)b.c.test();
但是 Eclipse 说从Optional<A>
到ArrayList<A>
的这种转换是不可能的。
我该如何解决这个问题?
【问题讨论】:
嗯,是的,Optional
不是ArrayList
,它们当然不能被强制转换。你是什么意思你“不能改变它”?什么是“它”,为什么不能改变?
Optianal<A>
是什么?
@ShadowRay docs.oracle.com/javase/8/docs/api/java/util/Optional.html
它不是一个集合,你所说的“转换”到 ArrayList 是什么意思?
@ShadowRay "什么是Optianal<A>
?"如果您的问题很笼统,那么:从 Guava 添加到 Java 8 是非常好的事情。它帮助我们避免空值 code.google.com/p/guava-libraries/wiki/…
【参考方案1】:
我假设您的预期语义是“如果存在该值,则返回一个包含单个项目的列表,否则返回一个空列表。”在这种情况下,我会建议如下:
ArrayList<A> result = new ArrayList<>();
b.c.test().ifPresent(result::add);
return result;
但是,我建议您的返回类型应为 List<A>
而不是 ArrayList<A>
,因为这样您就有机会在不更改调用者的情况下更改列表的类型。如果可选值不存在,它还允许您返回Collections.EMPTY_LIST
,这比创建不必要的ArrayList
更有效。
更新:Java 9 现在有了更简单的选择:
b.c.test().stream().collect(Collectors.toList());
更新:Java 16 甚至更简单的选择:
b.c.test().stream().toList();
【讨论】:
【参考方案2】:在这种情况下,完全可以不使用流:
public static <T> List<T> toList(Optional<T> opt)
return opt.isPresent()
? Collections.singletonList(opt.get())
: Collections.emptyList();
或者,使用函数式 API 的相同代码:
public static <T> List<T> toList(Optional<T> opt)
return opt
.map(Collections::singletonList)
.orElseGet(Collections::emptyList);
我更喜欢上面的变体,因为我确信它不会在 Java 堆上创建任何不必要的对象。
【讨论】:
【参考方案3】:如果每个人都坚持使用流来解决这个问题,那应该比使用ifPresent()
更地道
不幸的是,Java 8 没有Optional.stream()
方法,所以无法做到:
optional.stream().collect(Collectors.toList());
另请参阅:Using Java 8's Optional with Stream::flatMap
但在 JDK 9 中,它将被添加(并且该代码实际上已经在 Java 9 上运行)
Optional<Integer> o = Optional.empty();
final List<Integer> list = o.stream().collect(Collectors.toList());
System.out.println(list);
【讨论】:
【参考方案4】:return b.c.test()
.map(Arrays::asList).map(ArrayList::new)
.orElseGet(ArrayList::new);
如果可选项有一个值,它会将其“映射”到带有Arrays.asList
的List<A>
,然后通过new ArrayList<A>(List<A>)
构造函数映射到ArrayList
;否则它会通过空的构造函数产生一个空的ArrayList
。
这可以更明确地写成:
return b.c.test()
.map(value -> new ArrayList<A>(Arrays.asList(value)))
.orElseGet(() -> new ArrayList<A>());
【讨论】:
这似乎过于冗长了;这实际上是在滥用 Java 8 的函数式习语来完成一项非常简单的任务吗? 我不知道,我喜欢这种风格。我喜欢缺少临时变量。我认为复杂性是过于具体的返回类型的结果。如果是List
而不是ArrayList
,则可以更直接地写成:map(Collections::singletonList).orElseGet(Collections::emptyList)
。我喜欢它的外观。
IMO,@sprinter 答案中的临时变量使代码更具可读性(也更简洁)。 map
和 orElseGet
很有用——在正确的地方使用时。你的解决方案让我想起了Guava's Functionals Explained 中提出的警告——命令式代码应该是默认的,即使在 Java 8 中也是如此。【参考方案5】:
使用 Java9,您可以使用新添加的 Optional::stream API
来做到这一点:
Optional<List<A>> list;
List<A> collect = list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
【讨论】:
【参考方案6】:如果你使用 Guava 的Optional
,你可以这样做:
return new ArrayList<>(b.c.test().asSet());
这将从Optional
(如果存在)中提取值并将其添加到新的ArrayList
。
如果您使用的是 Java 8,@sprinter 的答案就是您所需要的。
【讨论】:
【参考方案7】:Optional 是一个容器对象,它可能包含也可能不包含非空值。
在 ArrayList 术语中,我会将其翻译为具有 0 或 1 个成员的数组。
public ArrayList<A> getMethods()
Optional<A> opt = b.c.test();
ArrayList<A> res = new ArrayList<>();
if ( opt.isPresent() )
res.add( opt.get() );
return res;
【讨论】:
Optional.empty()
是一个静态方法,它返回一个新的空Optional
。你想要Optional.isPresent()
。还有更简洁的方法可以做到这一点(@sprinter 的回答)。
@JoseAntonioDuraOlmos 我认为您的意思是if (opt.isPresent())
而不是if (!opt.isPresent())
。目前,当您尝试 get
并清空可选时,这将引发异常。
当然,盲目地将 !opt.empty() 更改为 !opt.isPresent() 会发生这种情况。【参考方案8】:
从 Java 9 开始
return b.c.test().stream()
.collect(toList());
导入静态 java.util.stream.Collectors.toList;
从 Java 8 开始
return b.c.test()
.map(List::of)
.orElse(emptyList());
导入静态 java.util.Collections.emptyList;
【讨论】:
以上是关于从 Optional<> 转换为 ArrayList<>的主要内容,如果未能解决你的问题,请参考以下文章
如何将 Optional<Dictionary<String, Any>> 转换为 Dictionary<String, Any> 以发送带有 json 参数的 A
将一个 std::optional 转换为另一个 std::optional
从非常量到常量模板参数的隐式转换在 boost::optional 中不起作用
无法将 '[String : AnyObject]?.Type' 类型(又名 'Optional<Dictionary<String, AnyObject>>.Type)的值转