0

我不想检查数组列表中是否存在具有值的项目,

我想知道性能的最佳代码,以获取数组列表中存在多少具有相同值的相同项目。

我可以做类似的事情

For Each s As string In myArrayList
[...]
Next

但是有更好的代码吗?

4

2 回答 2

4

听起来您想查看特定元素在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 兼容的答案

于 2012-03-23T16:10:20.283 回答
2

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
于 2012-03-23T16:16:23.470 回答