5

java.util.Spliterator 我在(Java 8)中偶然发现了一个有趣的细节。

方法 trySplit() 应该返回 Spliterator 的实例,或者null,如果它不能被拆分。Java 文档说以下内容:

 * @return a {@code Spliterator} covering some portion of the
 * elements, or {@code null} if this spliterator cannot be split.

在我看来,它是一个完美的使用场所java.util.Optional。根据javadoc:

 * A container object which may or may not contain a non-null value.

有什么原因,为什么没有使用 Optional ?

谷歌搜索并没有太大帮助,除了 lambda-dev 邮件列表中的这个问题,没有得到回答。

4

2 回答 2

10

它是这样的有几个原因。当然,从概念上讲,trySplit它可能会回归Optional<Spliterator<T>>,但有一些设计力量推动了这一点。

findFirst一个原因是,诸如return之类的Optional方法与诸如trySplitreturn value-or-null之类的方法之间存在差异。

  • 诸如此类findFirst的方法由应用程序代码调用并将值返回给应用程序代码。
  • 类似trySplit的方法由库代码调用并将值返回给代码。

JDK 类库的一个设计方面是库 API 被(或应该)设计为使应用程序代码更容易,并且库代码通常会变得更复杂,以使应用程序更简单。

的主要原因之一Optional是避免将空值从库传递到应用程序代码,因为不正确的空值处理是NullPointerExceptions 的常见来源。取而代之的是null,APIfindFirst将返回一个空的Optional,它由一组丰富的方法支持,例如orElsemapfilterflatMap等,这些方法为应用程序处理未找到的情况提供了很大的灵活性。

请注意,可以为空的返回值从trySplit相反的方向发展:从应用程序到库。

让应用程序代码向库传递或返回一个可为空的值比让应用程序从库中接收一个可为空的值更不容易出错。如果您正在编写一个应用程序并且 API 说您应该向库传递或返回 null,那么这不可能在您的代码中生成 NPE。实际上,API 中有很多地方(List.sort(null)想到)在nullAPI 中有特定的语义。

trySplit从库中相对较少的地方调用,并且库维护人员正在承担null在所有这些情况下正确处理的负担。

另一个主要考虑因素是性能。拆分是建立并行管道的关键路径。它是按顺序执行的,然后将工作移交给不同的线程以并行执行。根据Amdahl 定律,为了使并行性尽可能高效,您希望最小化顺序设置开销。

事实是 anOptional是一个盒子,将一个值装箱和拆箱是有成本的Optional。在某些情况下, JIT 编译器可能会优化它,但它可能不会。即使是这样,也有一段时间代码正在运行但Optional尚未优化。这是额外的开销。由于库代码愿意承担正确处理的负担,我们可以保证在这种情况下null根本不使用就不会产生装箱开销。Optional

于 2015-05-15T05:33:59.323 回答
7

Spliterator是内部流实现的一部分。它不应该用在Optional方便的业务逻辑中。它的主要目标是快速的相当低级的接口。所以没有理由在Optional那里。

您可能会争辩说,Optional通常可以通过 JIT 编译器消除。然而,情况并非总是如此。例如,Hotspot JIT 编译器中内联调用的默认最大深度为 10,并且通常的流处理具有更多的堆栈帧,因此即使是一个额外的堆栈帧也可能会阻止优化。

于 2015-05-12T15:20:58.007 回答