检查 Set 是不是不包含 null 的巧妙方法

Posted

技术标签:

【中文标题】检查 Set 是不是不包含 null 的巧妙方法【英文标题】: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. 【参考方案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 候选人吗?

以上是关于检查 Set 是不是不包含 null 的巧妙方法的主要内容,如果未能解决你的问题,请参考以下文章

检查 Sql Server Compact Database(.sdf) 是不是包含未连接的内容

检查数组是不是包含javascript中的null以外的内容?

编写 CHECK CONSTRAINT 的一种更好的方法,它检查一个值是不是不为空

Spring Data JPA 检查 SET 是不是包含对象

检查 arraytype 列是不是包含 null

set 不包含等于其成员之一的项目?