从 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&lt;A&gt; 作为返回类型的值。但我需要返回一个ArrayList&lt;A&gt;

所以,我尝试将其转换为:

public ArrayList<A> getMethods()
    return (ArrayList<A>)b.c.test();

但是 Eclipse 说从Optional&lt;A&gt;ArrayList&lt;A&gt; 的这种转换是不可能的。

我该如何解决这个问题?

【问题讨论】:

嗯,是的,Optional 不是ArrayList,它们当然不能被强制转换。你是什​​么意思你“不能改变它”?什么是“它”,为什么不能改变? Optianal&lt;A&gt; 是什么? @ShadowRay docs.oracle.com/javase/8/docs/api/java/util/Optional.html 它不是一个集合,你所说的“转换”到 ArrayList 是什么意思? @ShadowRay "什么是Optianal&lt;A&gt;?"如果您的问题很笼统,那么:从 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&lt;A&gt; 而不是 ArrayList&lt;A&gt;,因为这样您就有机会在不更改调用者的情况下更改列表的类型。如果可选值不存在,它还允许您返回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 中,它将被添加(并且该代码实际上已经在 J​​ava 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.asListList&lt;A&gt;,然后通过new ArrayList&lt;A&gt;(List&lt;A&gt;) 构造函数映射到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 答案中的临时变量使代码更具可读性(也更简洁)。 maporElseGet 很有用——在正确的地方使用时。你的解决方案让我想起了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)的值转

IDEA中Spring boot配置热部署

从 CArchive 读取 WORD 变量并同时转换为 int