【问题标题】:What's the best code for performance to check if a .net arraylist have two or more items with the same value?检查.net arraylist是否有两个或多个具有相同值的项目的最佳性能代码是什么?
【发布时间】:2012-03-23 16:03:07
【问题描述】:

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

我想知道性能的最佳代码,以获取具有相同值的相同项目在 arraylist 中的出现次数。

我可以做类似的事情

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

但是有更好的代码吗?

【问题讨论】:

  • Arraylist 在 .Net 2.0 及更高版本中应完全避免使用。没有充分的理由再使用它了。
  • 只是好奇@joel,你将如何实现一个字符串数组,例如具有与数组列表相同的功能?
  • 我会使用 List(Of String)

标签: .net vb.net for-loop arraylist items


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    Sort 你的ArrayList 首先使用BinarySearch。这应该比 Dictionary 方法提供更好的性能,因为您不需要创建另一个集合或完全循环现有的集合来查找项目。

    当然,这远非优雅和可读的代码,但它快速(约 2 秒,10 MM 查找)。

    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,例如List(Of String)


    编辑:因为我已经提到了通用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,例如 List(Of String)List(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 可以optimize the search。只是为了完整性。


    顺便说一句,@JaredPars Dictionary 在列表中有很多重复项时要快得多(因此字典像字母样本中一样小。

    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
    

    【讨论】:

      猜你喜欢
      • 2019-12-17
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 2015-01-25
      • 1970-01-01
      • 2019-05-16
      • 2015-06-02
      • 2017-11-02
      相关资源
      最近更新 更多