【问题标题】:Does checking if list is null on java affects on performance?检查 java 上的 list 是否为 null 会影响性能吗?
【发布时间】:2026-02-05 09:15:02
【问题描述】:

我知道这很简单,但我找不到任何问题。

当我检查一个列表是否为空然后对其进行迭代时,它对性能有何影响?

我有以下代码,我想知道对 getContainers() 的第二次调用是否再次执行该方法,或者编译器正在保存列表,因此它不必再次运行 getContainers()。

if (getContainers() != null)
{
    for (Container container : getContainers())
{...

如果这不是真的,我正在考虑做一些类似下面这段代码的事情,但它似乎很天真。

List<Container> listC = getContainers();
if (listC != null)
{
    for (Container container : listC)
{...

【问题讨论】:

  • 第二种方法只调用一次getContainers所以性能更好
  • 将简单的 System.out.println() 添加到 getContainers() 中,您将看到结果 =) 对我来说,第二个变体更好。
  • 检查对象是否为空对性能几乎没有影响。第二种方法将有更好的性能,因为它不会重复在getContainers() 方法中完成的操作;换句话说,它获得(或未能获得,在null 结果的情况下)列表仅一次;但这与检查 null 本身的性能影响无关。
  • 更好的解决方案是将getContainers() 更改为返回Collections.emptyList() 而不是null

标签: java performance list null


【解决方案1】:

第二个版本更好有两个原因:

  • 没那么重要:您提到的性能优势(如果编译器没有自动优化)
  • 更重要的是:如果在多线程环境中运行,对getContainers() 的两次调用可能不会给出相同的结果:第一个可能不为空,但第二个可能为空。

【讨论】:

  • 请注意,第二点中提到的可能性会禁止第一点中提到的编译器进行优化。
  • @skyking:不一定。我对 Java 编译器了解不多,但我知道其他编译器会根据多线程的存在与否做出不同的优化。我想到的是将来使用为单线程环境创建的代码可能会产生难以发现的错误。
  • 好的,但是 Java 编译器真的可以在编译时确定吗?我的意思是,如果它将它编译为.class 文件,然后将.class 文件与其他一些实际启动新线程的.class 文件一起使用,那么如果第一个假设第二个不应该创建,那将是一个问题线程。
  • 我希望这种优化会在 JIT 时间发生(如果有的话)。此时,信息可用。
【解决方案2】:

它很可能会调用getContainers 函数两次。除非知道它不能被覆盖,因此可以断定它没有副作用并返回相同的值。

因此,如您的第二个示例所示,这样做是合理的。但是请注意,它只会被调用两次,这通常不是尝试此优化的充分理由。

在尝试优化之前,您应该实际测量代码在优化前后花费的时间。之前是因为它会告诉您是否需要优化,之后是为了看到您实际上已经优化(我已经看到了一些增加执行时间的“优化”)。

【讨论】:

  • 由于您的回答涉及微优化,您可能需要包含this 链接:)
【解决方案3】:

这个问题被混淆了。事实是,您需要检查 getContainers() 是否在某个时候返回 null,除非您允许 NullPointerException,在这种情况下,您决定不冒险。在这两个示例中,您都在检查 null,因此此操作所用的时间完全相同。

您真正感兴趣的是,两次编写 getContainers() 是否会影响性能。如果您了解此方法除了在(可能是私有的)本地类中返回存储的对象之外没有做任何事情,那么您不应该担心任何性能,因为它是不引人注意的。如果它确实做了一些更复杂的事情,但可以被其他方法缓存,那么你应该研究另一种方法。两次调用它可能不是这个方法的错。如果有疑问,第二个例子保证它只被调用一次,代价是为它声明一个变量。

还需要注意的是,在增强的 for 循环中,

for (Container container : getContainers() )

getContainers() 只被调用一次,而不是 for 循环的每次迭代。它在内部检索列表并从中获取一个迭代器,该迭代器为每次迭代的容器分配一个值。唯一的问题是它不检查 NPE。

【讨论】: