4

我知道异常捕获可能很昂贵,但我想知道是否存在实际上比查找便宜的情况?

例如,如果我有一个大字典,我可以测试一个键是否存在:

If MyDictionary.ContainsKey(MyKey) Then _
  MyValue = MyDictionary(MyKey) ' This is 2 lookups just to get the value.

或者,我可以捕获一个异常:

Try
  MyValue = MyDictionary(MyKey) ' Only doing 1 lookup now.
Catch(e As Exception)
  ' Didn't find it.
End Try

异常捕获总是比上面的查找更昂贵,还是在某些情况下更便宜?

4

3 回答 3

5

关于字典查找的事情是它们发生在恒定或接近恒定的时间。无论您的字典包含一个项目还是一百万个项目,您的计算机所花费的时间大致相同。我提出这个问题是因为您担心在大字典中进行两次查找,而现实情况是,这与在小型字典中进行两次查找并没有太大区别。作为旁注,这里的一个含义是字典并不总是小型集合的最佳选择,尽管我通常发现额外的清晰度仍然超过这些小型集合的任何性能问题。

决定字典查找速度的因素之一是为特定对象生成哈希值所需的时间。有些对象可以比其他对象快得多。这意味着这里的答案取决于字典中的对象类型。因此,唯一确定的方法是构建一个版本,对每种方法进行数十万次测试,以找出哪个更快地完成集合。

这里要记住的另一个因素是,它主要只是 Catch 块在异常处理方面很慢,因此您需要寻找与您在生产中期望的合理匹配的查找命中和未命中的正确组合。出于这个原因,您无法在此处找到一般指南,或者如果您这样做,则很可能是错误的。如果您很少有遗漏,那么我希望异常处理程序做得更好(并且,由于遗漏有点,嗯,例外,它也是正确的解决方案)。如果你经常错过,我可能更喜欢不同的方法

当我们在做的时候,让我们不要忘记Dictionary.TryGetValue()

于 2012-12-03T18:19:07.173 回答
1

我测试了ContainsKeyvsTryCatch的性能,结果如下:

附加调试器:

在此处输入图像描述

没有附加调试器:

在此处输入图像描述

Sub Main仅使用以下代码在控制台应用程序的发布版本上进行了测试。ContainsKey使用调试器的速度大约是 37000 倍,而在没有附加调试器的情况下仍然快 355 倍,因此即使您进行两次查找,它也不会像您需要捕获额外的异常那样糟糕。这是假设您经常寻找丢失的密钥。

Dim dict As New Dictionary(Of String, Integer)
With dict
  .Add("One", 1)
  .Add("Two", 2)
  .Add("Three", 3)
  .Add("Four", 4)
  .Add("Five", 5)
  .Add("Six", 6)
  .Add("Seven", 7)
  .Add("Eight", 8)
  .Add("Nine", 9)
  .Add("Ten", 10)
End With

Dim stw As New Stopwatch
Dim iterationCount As Long = 0
Do
  stw.Start()
  If Not dict.ContainsKey("non-existing key") Then 'always true
    stw.Stop()
    iterationCount += 1
  End If
  If stw.ElapsedMilliseconds > 5000 Then Exit Do
Loop

Dim stw2 As New Stopwatch
Dim iterationCount2 As Long = 0
Do
  Try
    stw2.Start()
    Dim value As Integer = dict("non-existing key") 'always throws exception
  Catch ex As Exception
    stw2.Stop()
    iterationCount2 += 1
  End Try
  If stw2.ElapsedMilliseconds > 5000 Then Exit Do
Loop

MsgBox("ContainsKey: " & iterationCount / 5 & " per second, TryCatch: " & iterationCount2 / 5 & " per second.")
于 2012-12-03T18:44:25.350 回答
0

如果您试图在某种不易搜索的数据结构中找到一个项目(例如,在 100K 个项目的未索引字符串数组中找到一个包含单词“flabbergasted”的项目,那么是的,让它抛出异常将是更快,因为您只进行一次查找。如果您首先检查该项目是否存在,然后获取该项目,您将进行两次查找。但是,在您的示例中,您正在查找一个项目在字典(哈希表)中,它应该非常快,因此进行两次查找可能比让它失败更快,但是如果不测试它就很难说。这完全取决于对象的哈希值有多快计算以及列表中有多少项共享相同的哈希值。

正如其他人所建议的那样,对于DictionaryTryGetValue将提供两种方法中最好的方法。其他列表类型提供类似的功能。

于 2012-12-03T18:22:25.183 回答