【问题标题】:Why does this code sometimes throw a NullPointerException?为什么这段代码有时会抛出 NullPointerException?
【发布时间】:2010-03-16 21:16:47
【问题描述】:

考虑以下 Java 源代码:

if( agents != null ) {
  for( Iterator iter = agents.keySet().iterator(); iter.hasNext(); ) {
    // Code that uses iter.next() ...
    //
  }
}

agentsHashMap

为什么for 语句有时会抛出NullPointerException

谢谢。

【问题讨论】:

  • @Bart van Heukelom:NullPointerException

标签: java hashmap


【解决方案1】:

线程安全

如果您的代码是多线程的,那么这是可能的。例如:

public class C {
  private Hashtable agents = new Hashtable();

  public iterate() {
    if( agents != null ) {
      for (Iterator iter = agents.keySet().iterator(); iter.hasNext();) {
        // Code goes here
      }
    }
}

如果另一个线程在if 语句执行之后(但在for 循环之前)立即将agents 设置为null,那么您将获得NullPointerException。通过使用访问器(结合惰性初始化)来避免这种情况。

另外,正如其他人所提到的,如果可能,请避免使用泛型来支持此类循环构造。有关详细信息,请参阅其他答案。

访问器提供保护

如果您始终使用以下模式,您的源代码中将永远不会有NullPointerExceptions(另一方面,第三方代码可能存在导致您的代码间接失败的问题,这是无法轻易避免的)。

public class C {
  private Hashtable agents;

  private synchronized Hashtable getAgents() {
    if( this.agents == null ) {
      this.agents = new Hashtable();
    }

    return this.agents;
  }

  public iterate() {
    Hashtable agents = getAgents();

    for (Iterator iter = agents.keySet().iterator(); iter.hasNext();) {
      // Code goes here
    }
  }
}

遍历代理的代码不再需要检查null。由于许多原因,此代码更加健壮。您可以将Hashmap(或任何其他抽象数据类型,例如ConcurrentHashMap<K,V>)替换为Hashtable

开闭原则

如果您觉得自己的时间特别慷慨,您可以这样做:

public class C {
  private Hashtable agents;

  private synchronized Hashtable getAgents() {
    if( this.agents == null ) {
      this.agents = createAgents();
    }

    return this.agents;
  }

  public iterate() {
    Iterator i = getAgentKeyIterator();

    while( i.hasNext() ) {
      // Code that uses i.next() ...
    }
  }

  protected Hashtable createAgents() {
    return new Hashtable();
  }

  private Iterator getAgentKeyIterator() {
    return getAgentKeys().iterator();
  }

  private KeySet getAgentKeys() {
    return getAgents().keySet();
  }
}

这将允许子类(由其他开发人员编写)替换他们自己正在使用的抽象数据类型的子类(允许系统更灵活地与Open-Closed Principle保持一致),而无需修改(或复制/浪费)你的原创作品。

【讨论】:

  • 我认为ConcurrentHashMap<K, V>Hashtable 更可取。
  • 我认为多线程环境可能的解释是合理的,让我再多解释一下,谢谢。
【解决方案2】:

那不应该是一个while循环吗?

if (agents != null) {
    Iterator iter = agents.keyset().iterator();
    while (iter.hasNext()) {
        //some stuffs here
    }
}

还是每个都有一个?

if (agents != null) {
    //Assuming the key is a String
    for (String key : agents.keyset()) {
        //some stuffs here
    }
}

【讨论】:

  • 我会说 for 循环更适合迭代器。我不会使用 while 循环,因为迭代器在 while 之后对所有代码都是可见的。
  • @Steve Kuo:就我个人而言,我最喜欢 for each style 循环,使用它而不是直接处理Iterators。
  • 同意,因为每个都比直接使用 Iterator 更好(除非你需要调用 Iterator.remove())。
【解决方案3】:

确保不要在循环内将 iter 设置为 null。

【讨论】:

    猜你喜欢
    • 2012-11-25
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多