【问题标题】:Neat way to check whether a Set does not contain a null检查 Set 是否不包含 null 的巧妙方法
【发布时间】:2012-02-05 22:22:36
【问题描述】:

我有一个方法,它被赋予了 Set 的对象。它委托给的方法要求Set 不包含任何空元素。我想check the precondition Set 在委托之前的方法中早期不包含空元素。这样做的明显代码是这样的:

public void scan(Set<PlugIn> plugIns) {
   if (plugIns == null) {
      throw new NullPointerException("plugIns");
   } else if (plugIns.contains(null)) {
      throw new NullPointerException("plugIns null element");
   }
   // Body
 }

但这是不正确的,因为如果Set 实现本身 不允许空元素,Set.contains() 可能会抛出NullPointerException。在这种情况下捕获然后忽略NullPointerException 将起作用but would be inelegant。有没有一种简洁的方法来检查这个前提条件?


Set 接口是否存在设计缺陷?如果Set 实现可能永远不会包含空值,为什么不要求Set.contains(null) 始终返回false?或者有一个isNullElementPermitted() 谓词?

【问题讨论】:

  • 如果您有这样的特定要求,请继承 Set 并禁止 null puts。另外,我不会在这里使用else
  • 我同意这非常烦人。您允许使用通用 Set 创建您的类,但要确保根据您的类合同它不包含 nulls。然后,如果有人实际上传递了不允许 nulls 的 Set,那么您的安全 contains 检查会抛出 NPE,因为它在规范中。这是设计错误恕我直言,因为调用者对此无能为力(即,无法询问 Set 是否允许空值),更不用说在这种情况下抛出 NPE 而不是仅仅返回似乎很愚蠢false.

标签: java null set nullpointerexception


【解决方案1】:

最简单的方法是枚举 Set 并检查空值。

public void scan(Set<PlugIn> plugIns) {
  if (plugIns == null) throw new NullPointerException("plugIns");
  for (PlugIn plugIn : plugIns) {
    if (plugIn == null) throw new NullPointerException("plugIns null element");
  }
}

【讨论】:

  • 简单,但 O(N) 复杂,不利于前置条件检查。
  • 引用另一句话:“预优化是万恶之源”。您确定这是性能瓶颈吗?鉴于您正在做的事情,我猜想其他地方存在架构问题。
  • 我担心的是不是过早的优化。一个简洁的解决方案应该是通用的并且相当有效。每当有人在没有首先测量性能的情况下使用HashSet 并获得快速Set.contains() 的好处时,他们并没有进行过早的优化。 programmers.stackexchange.com/questions/79946/….
【解决方案2】:

plugIns 创建一个HashSet 并检查null 是否存在

public void scan(Set<PlugIn> plugIns) {
  if (plugIns == null) throw new NullPointerException("plugIns");
  Set<PlugIn> copy = new HashSet<PlugIn>(plugIns);
  if (copy.contains(null)) {
      throw new NullPointerException("null is not a valid plugin");
  }
}

【讨论】:

  • 简单,但 O(N) 复杂并创建一个新对象,这不利于前置条件检查。
  • 您每秒扫描几千次以查找新插件? ;) 根据我的经验,复制或创建新集合很少是性能问题。也许你应该阻止null 被添加到plugIns 的来源(对我来说,这听起来像是你在修复别人的错误代码,除非有正当理由证明null-plugin 的存在)
  • 我这样做不是为了解决错误代码。我想这样做是因为尽早发现故障会更好。想象这是一个 API 方法:我们不会控制调用代码,但可能希望提供良好的诊断。
  • "您每秒扫描几千次以查找新插件?"好点,您的解决方案对于我所拥有的 specific 案例具有足够的性能。但我也想知道如何在其他情况下进行检查,在这些情况下,性能会更重要。
  • 好的,我明白你的意思了。但是让我们假设这是实际 API 的一部分:在这种情况下,它取决于调用者和被调用者之间的接口/契约。如果null 被允许作为集合的一部分,您可以在//body 中使用简单的if 处理它(因为我们不知道如何处理Null-Plugins)。如果null 不允许在集合中,则根本不需要检查null 除非您怀疑API 有错误。恕我直言,这与个人品味有很大关系,因此没有对错之分。希望有帮助;)
【解决方案3】:

如果抛出 NullPointerException 并忽略它,只需捕获它:

public void scan(Set<PlugIn> plugIns) {
    if (plugIns == null) {
        throw new NullPointerException("plugIns");
    }

    NullPointerException e = null;
    try {
        if (plugIns.contains(null)) {
            // If thrown here, the catch would catch this NPE, so just create it
            e = new NullPointerException("plugIns null element");
        }
    } catch (NullPointerException ignore) { }

    if (e != null) {
        throw e;
    }
    // Body
}

如果抛出,这只会产生很小的开销,但如果你不使用异常(尤其是 strack 跟踪),它实际上是非常轻量级的。

【讨论】:

  • 你不认为你的 NPE 候选人更多的是 IllegalArgumentException 候选人吗?
猜你喜欢
  • 1970-01-01
  • 2020-05-25
  • 1970-01-01
  • 2021-10-30
  • 2018-07-02
  • 1970-01-01
  • 1970-01-01
  • 2013-12-19
  • 2023-03-07
相关资源
最近更新 更多