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

Neat way to check whether a Set does not contain a null

提问人:Raedwald 提问时间:1/9/2012 最后编辑:CommunityRaedwald 更新时间:1/9/2012 访问量:18279

问:

我有一个给定对象的方法。它委托给的方法要求 不包含任何 null 元素。我想在委托之前的方法中尽早检查不包含空元素的前提条件。这样做的明显代码是这样的:SetSetSet

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
 }

但这是不正确的,因为如果实现本身不允许 null 元素,Set.contains() 可能会抛出一个。在这种情况下,捕捉然后忽略会起作用,但会不优雅。有没有一种巧妙的方法来检查这个前提条件?NullPointerExceptionSetNullPointerException


界面是否存在设计缺陷?如果实现可能永远不会包含 null,为什么不改为 require to always return 呢?还是有谓语?SetSetSet.contains(null)falseisNullElementPermitted()

java nullpointerexception

评论

0赞 Dave Newton 1/9/2012
如果您有这样的特定要求,请对看跌期权进行子类化并禁止看跌期权。另外,我不会在这里使用。Setnullelse
2赞 john16384 3/26/2019
我同意这非常烦人。您允许使用泛型 创建类,但希望确保它不包含类合约中的 s。然后,如果有人真的通过了不允许 s 的 s,您的安全检查会抛出 NPE,因为它在规范中。恕我直言,这是设计错误,因为调用者对此无能为力(即,无法询问 Set 是否允许 null),更不用说在这种情况下抛出 NPE 而不是仅仅返回似乎是愚蠢的。SetnullSetnullcontainsfalse

答:

5赞 Dan Hardiker 1/9/2012 #1

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

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");
  }
}

评论

1赞 Raedwald 1/9/2012
简单,但复杂,这对前提条件检查不利。O(N)
1赞 Dan Hardiker 1/9/2012
引用另一个人的话:“预优化是万恶之源”。您确定这是性能瓶颈吗?鉴于你正在做的事情,我猜其他地方存在架构问题。
0赞 Raedwald 1/9/2012
我担心的不是过早的优化。一个简洁的解决方案应该是通用的,并且相当有效。每当有人在没有首先衡量性能的情况下使用,并获得快速的好处时,他们就不会进行过早的优化。programmers.stackexchange.com/questions/79946/....HashSetSet.contains()
3赞 Matt 1/9/2012 #2

创建一个 from 并检查是否存在HashSetplugInsnull

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");
  }
}

评论

1赞 Raedwald 1/9/2012
简单,但很复杂,并创建一个新对象,这不利于前提条件检查。O(N)
1赞 Matt 1/9/2012
您每秒扫描多少次新插件?;)根据我的经验,复制或创建新集合很少是性能问题。也许你应该防止被添加在哪里(对我来说,这听起来像是你在修复别人的错误代码,除非有正当理由存在-plugin)nullplugInsnull
0赞 Raedwald 1/9/2012
我这样做不是为了解决错误的代码。我想这样做,因为最好及早发现故障。想象一下,这是一种 API 方法:我们无法控制调用代码,但可能希望提供良好的诊断。
0赞 Raedwald 1/9/2012
“您每秒扫描多少次新插件?”好点子,您的解决方案对于我的特定情况将具有足够的性能。但我也想知道我如何在其他地方进行检查,那里的性能会更重要。
0赞 Matt 1/9/2012
好的,我明白你的意思了。但是,让我们假设这是实际 API 的一部分:在这种情况下,它取决于调用方和被调用方之间的接口/合约。如果允许作为集合的一部分,你可以用一个简单的(因为我们不知道如何处理 Null-Plugins)来处理它。如果不允许在集合中,则根本不需要检查,除非您怀疑 API 有问题。恕我直言,这与个人品味有很大关系,所以没有对错之分。希望能;)null//bodyifnullnull
2赞 Bohemian 1/9/2012 #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
}

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

评论

0赞 unknown_boundaries 4/16/2015
你不认为你的NPE候选人更像是IllegalArgumentException的候选人吗?