我不想检查数组列表中是否存在具有值的项目,
我想知道性能的最佳代码,以获取数组列表中存在多少具有相同值的相同项目。
我可以做类似的事情
For Each s As string In myArrayList
[...]
Next
但是有更好的代码吗?
听起来您想查看特定元素在ArrayList
. 如果是这样,请尝试以下
Dim table As New Dictionary(Of String, Integer)
For Each s As String in myArrayList
Dim count As Integer = 0
If table.TryGetValue(s, count) Then
table(s) = count + 1
Else
table(s) = 1
End If
Next
For Each pair in table
If pair.Value >= 2 Then
Console.WriteLine(pair.Key)
End If
Next
注意:这里假设您使用的是 Visual Studio 2005 或更高版本。如果不是,请告诉我,我将提供 2003 兼容的答案
Sort
你的ArrayList
第一个然后使用BinarySearch
. 这应该比 Dictionary 方法提供更好的性能,因为您不需要创建另一个集合或完全循环现有的集合来查找项目。
当然,这远不是优雅和可读的代码,但它很快(10 MM 查找约 2 秒)。
Dim letters = New ArrayList() From {"A", "B", "C", "D", "A", "C", "E", "cC", "C", "E", "A", "c", "C", "F", "C"}
letters.Sort() ' just needed once and only if it's not already sorted
Dim lookupItem = "C"
Dim itemCount = 0 ' correct result: 5 (case-sensitive)
Dim index = letters.BinarySearch(lookupItem)
If index > -1 Then
Dim nextIndex = index
While letters(nextIndex).Equals(lookupItem)
itemCount += 1
nextIndex += 1
End While
If index > 0 Then
' look into the other direction since BinarySearch
' does not necessarily return the first index
' in this example index is 6 instead of 5
Dim prevIndex = index - 1
While letters(prevIndex).Equals(lookupItem)
itemCount += 1
prevIndex -= 1
End While
End If
End If
请注意,您value
应该实现的类型IComparable
或您定义可以传递给的自定义比较器BinarySearch
。
顺便说一句,ArrayList
您应该使用强绑定通用列表而不是 a ,例如 a List(Of String)
。
编辑:因为我已经提到了 generic Lists
,所以我将向您展示另一种方法,使用Lists(Of T)
已经包装在一个方便的扩展方法中:
Public Module ListExtensions
<Runtime.CompilerServices.Extension()> _
Public Function ItemCount(Of T)(ByVal sortedList As List(Of T), item As T) As Int32
Dim count = 0
Dim index = sortedList.BinarySearch(item)
Dim nextIndex = index
If index > -1 Then
While nextIndex < sortedList.Count AndAlso sortedList(nextIndex).Equals(item)
count += 1
nextIndex += 1
End While
If index > 0 Then
Dim prevIndex = index - 1
While prevIndex > 0 AndAlso sortedList(prevIndex).Equals(item)
count += 1
prevIndex -= 1
End While
End If
End If
Return count
End Function
End Module
现在,您可以在任何地方获取任何列表中任何对象的 itemcount,例如 aList(Of String)
和 aList(Of Integer)
包括几个测量值:
Const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
Dim rnd As New Random()
Dim letters = Enumerable.Range(1, 100000).Select(Function(i) chars(rnd.Next(0, chars.Length)).ToString).ToList
Dim letterTime = New Stopwatch
letterTime.Start()
letters.Sort()
For i = 1 To 100000
Dim count = letters.ItemCount("C")
Next
letterTime.Stop()
Dim numbers = Enumerable.Range(1, 100000).Select(Function(i) rnd.Next(100000)).ToList()
Dim numberTime = New Stopwatch
numberTime.Start()
numbers.Sort()
For i = 1 To 100000
Dim count = numbers.ItemCount(4711)
Next
numberTime.Stop()
' measure the LINQ Where-Extension
Dim letterTimeWhere = New Stopwatch
letterTimeWhere.Start()
For i = 1 To 100000
Dim count = letters.Where(Function(str) str.Equals("C")).Count()
Next
letterTimeWhere.Stop()
Dim numberTimeWhere = New Stopwatch
numberTimeWhere.Start()
For i = 1 To 100000
Dim count = numbers.Where(Function(int) int = 4711).Count()
Next
numberTimeWhere.Stop()
在包含 100000 个项目的列表中搜索 100000 个字符串/整数的结果。
Dim time = String.Format("String(Binary): {0} Numbers(Binary): {1} String(LINQ): {2} Numbers(LINQ): {3}", letterTime.Elapsed.ToString, numberTime.Elapsed.ToString, letterTimeWhere.Elapsed.ToString, numberTimeWhere.Elapsed.ToString)
' String(Binary): 00:00:05.2602861 Numbers(Binary): 00:00:00.0350816
' String(LINQ) : 00:04:56.8772996 Numbers(LINQ) : 00:01:43.2139190
' => Binary 55 x faster => Binary 2950 x faster
注意:LINQ 比较肯定是不公平的,因为Where
需要循环每个项目并且BinarySearch
可以优化搜索。只是为了完整性。
顺便说一句,Dictionary
当列表中有很多重复项时,@JaredPars 的速度要快得多(因此字典像字母样本中一样小。
String(Dict) : 00:00:00.0224329 Numbers(Dict): 00:00:00.0216544
我承认失败;)
这是他的字典作为扩展名:
<Runtime.CompilerServices.Extension()> _
Public Function ToCountLookup(Of T)(ByVal list As List(Of T)) As Dictionary(Of T, Int32)
Dim table As New Dictionary(Of T, Integer)
For Each s As T In list
Dim count As Int32 = 0
If table.TryGetValue(s, count) Then
table(s) = count + 1
Else
table(s) = 1
End If
Next
Return table
End Function
您可以通过这种方式使用它,TryGetValue
因为Dictionary
可能不包含该密钥:
Dim letterLookuptable = letters.ToCountLookup()
For i = 1 To 100000
Dim count = 0
letterLookuptable.TryGetValue("C", count)
Next
Dim intLookuptable = numbers.ToCountLookup()
For i = 1 To 100000
Dim count = 0
intLookuptable.TryGetValue(4711, count)
Next