检查 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
创建您的类,但要确保根据您的类合同它不包含 null
s。然后,如果有人实际上传递了不允许 null
s 的 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 的一种更好的方法,它检查一个值是不是不为空