字典,集合和数组的比较

Har*_*nch 20 excel vba excel-vba access-vba

我试图找出字典与集合和数组相比的相对优势和特性.

我在这里找到了一篇优秀的文章,但找不到比较所有各种功能的简单表格.

有谁知道吗?

Har*_*nch 41

请参阅下表,以便对集合和词典进行有用的比较.

(该表总结了此页面,直至"早期和晚期绑定"部分.仅供参考,该页面还提供了有关使用词典的更多详细信息)

总之,通常最好使用字典或数组.

在考虑使用集合时,如果大小没有变化,或者很少变化,则使用数组可能更合适.在这种情况下,数组可能比集合更有效,因为数组非常有效地一次填充和检索所有项目(例如,范围到数组和数组返回到范围).

另请注意:

与数组相比,集合为添加和插入项目以及通过其访问和删除项目提供了良好的性能.但是,如果要通过索引访问项目,则性能很差.有关有效执行此操作的信息,请参阅此处,其中还讨论了这些列表对象的内部工作方式.

这个cpearson页面有非常有用的代码,用于处理字典,集合和数组(对它们进行排序,并将它们转换为彼此!)

来自cpearson页面的一些文字:

Collection对象和Dictionary对象对于存储相关数据组非常有用.在其他条件相同的情况下,我使用Dictionary对象而不是Collection对象,因为您可以访问(读取,写入,更改)与Dictionary中的Item关联的Key属性.在相当差的对象设计中,Collection中项的Key是只写的.将项添加到集合时,可以为项指定项,但无法检索与项关联的项,也无法(直接)确定集合中是否存在项.字典非常友好,用钥匙打开.字典也比集合快得多.

为什么阵列是一个糟糕的选择.由于每个Redim将整个内存块复制到更大的位置,因此在重新调整大小和在中间插入项目时,数组要慢得多,如果使用Preserve,则所有值也会被复制.这可能转化为每次操作的感知缓慢 - 在潜在的应用中)

VBA中的集合与词典

Feature                 | COLLECTION | DICTIONARY | Remark
------------------------+------------+------------+--------------------------------
Usually faster          |            |     X      | 
------------------------+------------+------------+--------------------------------
Supported by VB Script  |            |     X      | Collections do not exist in VBS.
------------------------+------------+------------+--------------------------------
                        |            |            | Dicts: Add ref to Miscrosoft 
Native to VBA           |     X      |            | Scripting Library. Usage:
                        |            |            | Dim MyDict As Scripting.Dictionary
                        |            |            | Set MyDict = New Scripting.Dictionary
------------------------+------------+------------+--------------------------------
Can change Keys and     |            |            | Dict properties are writable.
Items                   |            |     X      | For collections, remove the item
                        |            |            | and add a new item.
------------------------+------------+------------+--------------------------------
                        |            |            | A collection enumerates its items:
                        |            |            |  For Each x In MyCollection
                        |            |            |      Debug.Print x
Enumerated              |     X      |     X      |  Next x
                        |            |            | A dict enumerates its keys:
                        |            |            |  For Each x In MyDictionary
                        |            |            |      Debug.Print MyDictionary.Item(x)
                        |            |            |  Next x
------------------------+------------+------------+--------------------------------
                        |            |            | A 1-d array of keys 
Directly output to      |            |            | and items can be returned by 
array                   |            |     X      | dict methods .Keys and .Items.
                        |            |            | (The array is zero-based even 
                        |            |            |  with Option Base 1.)
------------------------+------------+------------+--------------------------------
Retrieve and access     |     X      |     X      |
items                   |            |            |  
------------------------+------------+------------+--------------------------------
Add items               |     X      |     X      |
------------------------+------------+------------+--------------------------------
Implicitly add items    |            |     X      | Dicts can implicitly add items 
                        |            |            | using .Item property.
------------------------+------------+------------+--------------------------------
Remove items            |     X      |     X      |
------------------------+------------+------------+--------------------------------
Remove all items in     |            |            | With collections, each item must
one step                |            |     X      | be removed in turn, or the 
                        |            |            | collection destroyed and recreated.
------------------------+------------+------------+--------------------------------
Count items             |     X      |     X      |
------------------------+------------+------------+--------------------------------
Return item using key   |     X      |     X      |
as lookup value         |            |            |
------------------------+------------+------------+--------------------------------
Return item using       |            |            |
ordinal position        |     X      |   (Slow)   |
as lookup value         |            |            |
------------------------+------------+------------+--------------------------------
Return ordinal          |            |            |
position using item     |     X      |     ??     |
as lookup value         |            |            |
------------------------+------------+------------+--------------------------------
Retrieve and access     |            |     X      | Collection keys only used to
keys                    |            |            | look up data, not retrievable.
------------------------+------------+------------+--------------------------------
Keys optional           |     X      |            | Big + of collections, assuming keys
                        |            |            | are not needed. (Access via index.)
------------------------+------------+------------+--------------------------------
Case sensitivity        |            |     X      |
optional                |            |            |  
------------------------+------------+------------+--------------------------------
                        |            |            | Collection keys must be strings.
Keys can be any type    |            |     X      | Dict keys can have any type
                        |            |            | (except arrays), incl. mixed types.
------------------------+------------+------------+--------------------------------
Keys must be unique     |     X      |     X      |
------------------------+------------+------------+--------------------------------
                        |            |            | * For collections, add code:
                        |            |            |  Public Function _
                        |            |            |     Contains(col As Collection, _
Supports .Exists method |  Remark*   |     X      |     key As Variant) As Boolean
                        |            |            |     On Error Resume Next
                        |            |            |     col(key)
                        |            |            |     Contains = (Err.Number = 0)
------------------------+------------+------------+--------------------------------
Preserve key order when |            |     X      | This is because collection keys 
sorting by item value   |            |            | are write-only, not read. Poor design!
Run Code Online (Sandbox Code Playgroud)

  • 我最喜欢的Dicts功能是我可以将值或键输出为数组.如果这张表不是图像,我会添加该功能,但唉...... (3认同)

pau*_*ica 16

Option Explicit

Sub CollectionsVSdictionaries() ' Requires ref to "Microsoft Scripting Runtime" Library
    Dim c As Collection         ' TypeName 1-based indexed
    Dim d As Dictionary         ' 0-based arrays
    Set c = New Collection      ' or: "Dim c As New Collection"
    Set d = New Dictionary      ' or: "Dim d As New Dictionary"

    c.Add Key:="A", Item:="AA": c.Add Key:="B", Item:="BB": c.Add Key:="C", Item:="CC"
    d.Add Key:="A", Item:="AA": d.Add Key:="B", Item:="BB": d.Add Key:="C", Item:="CC"

    Debug.Print TypeName(c)    ' -> "Collection"
    Debug.Print TypeName(d)    ' -> "Dictionary"

    Debug.Print c(3)            ' -> "CC"
    Debug.Print c("C")          ' -> "CC"
    'Debug.Print c("CC")       ' --- Invalid ---

    Debug.Print d("C")          ' -> "CC"
    Debug.Print d("CC")        ' Adds Key:="CC", Item:=""
    Debug.Print d.Items(2)      ' -> "CC"
    Debug.Print d.Keys(2)       ' -> "C"
    Debug.Print d.Keys()(0)     ' -> "A"    - Not well known ***************************
    Debug.Print d.Items()(0)    ' -> "AA"   - Not well known ***************************

    'Collection methods:
    '    .Add                   ' c.Add Item, [Key], [Before], [After] (Key is optional)
    '    .Count
    '    .Item(Index)           ' Default property;   "c.Item(Index)" same as "c(Index)"
    '    .Remove(Index)
    'Dictionary methods:
    '    .Add                   ' d.Add Key, Item (Key is required, and must be unique)
    '    .CompareMode           ' 1. BinaryCompare     - case-sensitive   ("A" < "a")
    '    .CompareMode           ' 2. DatabaseCompare   - MS Access only
    '    .CompareMode           ' 3. TextCompare       - case-insensitive ("A" = "a")
    '    .Count
    '    .Exists(Key)           ' Boolean **********************************************
    '    .Item(Key)
    '    .Items                 ' Returns full array: .Items(0)(0)
    '    .Key(Key)
    '    .Keys                  ' Returns full array: .Keys(0)(0)
    '    .Remove(Key)
    '    .RemoveAll             ' ******************************************************
End Sub
Run Code Online (Sandbox Code Playgroud)


小智 9

关于集合与字典的性能,我发现写入字典的性能与写入集合的性能相似,从字典中读取的时间大约是从集合中读取的时间的两倍。首先创建字典比创建集合慢。

这些是我对字典/集合进行 100,000 次读取、写入和创建迭代后得到的结果:

Creating Multiple Dictionaries:   731ms
Writing To Dictionary:            494ms
Reading From Dictionary:           65ms

Creating Multiple Collections:     29ms
Writing To Collection:            459ms
Reading From Collection:           26ms
Run Code Online (Sandbox Code Playgroud)

请注意,添加对 Microsoft Scripting Runtine 的引用可提高创建多个字典的速度(此处为 495 毫秒)。

这是我用来测试的代码:

Option Explicit

Private p_lngTestCount As Long

Sub SetUp()
  p_lngTestCount = 100000
End Sub

Sub TestAll()
  CreatingMultipleDictionaries
  WritingToDictionary
  ReadingFromDictionary

  CreatingMultipleCollections
  WritingToCollection
  ReadingFromCollection
End Sub

Sub CreatingMultipleDictionaries()

  Const sSOURCE As String = "CreatingMultipleDictionaries"

  Dim oPerfMon As CDevPerformanceMonitor
  Set oPerfMon = New CDevPerformanceMonitor
  Dim i As Long
  Dim dcTest As Dictionary
  SetUp

  Dim dblTimeElapsed As Double
  oPerfMon.StartCounter

  For i = 0 To p_lngTestCount
    'Set dcTest = CreateObject("Scripting.Dictionary")
    Set dcTest = New Dictionary
  Next i

  dblTimeElapsed = oPerfMon.TimeElapsed

  Debug.Print sSOURCE & ": " & p_lngTestCount & " iterations. " & vbCrLf & _
              "Time elapsed: " & Round(dblTimeElapsed, 0) & "ms" & vbCrLf
End Sub

Sub CreatingMultipleCollections()

  Const sSOURCE As String = "CreatingMultipleCollections"

  Dim oPerfMon As CDevPerformanceMonitor
  Set oPerfMon = New CDevPerformanceMonitor
  Dim i As Long
  Dim colTest As Collection
  SetUp

  Dim dblTimeElapsed As Double
  oPerfMon.StartCounter

  For i = 0 To p_lngTestCount
    Set colTest = New Collection
  Next i

  dblTimeElapsed = oPerfMon.TimeElapsed

  Debug.Print sSOURCE & ": " & p_lngTestCount & " iterations. " & vbCrLf & _
              "Time elapsed: " & Round(dblTimeElapsed, 0) & "ms" & vbCrLf
End Sub

Sub WritingToDictionary()

  Const sSOURCE As String = "WritingToDictionary"

  Dim oPerfMon As CDevPerformanceMonitor
  Set oPerfMon = New CDevPerformanceMonitor
  Dim i As Long
  Dim dcTest
  SetUp

  Set dcTest = CreateObject("Scripting.Dictionary")
  'Set dcTest = New Dictionary

  Dim dblTimeElapsed As Double
  oPerfMon.StartCounter

  For i = 0 To p_lngTestCount
   ' Performance about the same for both ways:
    dcTest.Item(CStr(i)) = "test"
    'dcTest.Add CStr(i), "test"
  Next i

  dblTimeElapsed = oPerfMon.TimeElapsed

  Debug.Print sSOURCE & ": " & p_lngTestCount & " iterations. " & vbCrLf & _
              "Time elapsed: " & Round(dblTimeElapsed, 0) & "ms" & vbCrLf
End Sub

Sub WritingToCollection()

  Const sSOURCE As String = "WritingToCollection"

  Dim oPerfMon As CDevPerformanceMonitor
  Set oPerfMon = New CDevPerformanceMonitor
  Dim i As Long
  Dim colTest As Collection
  SetUp

  Dim dblTimeElapsed As Double
  Set colTest = New Collection

  oPerfMon.StartCounter

  For i = 0 To p_lngTestCount
    colTest.Add "test", CStr(i)
  Next i

  dblTimeElapsed = oPerfMon.TimeElapsed

  Debug.Print sSOURCE & ": " & p_lngTestCount & " iterations. " & vbCrLf & _
              "Time elapsed: " & Round(dblTimeElapsed, 0) & "ms" & vbCrLf
End Sub

Sub ReadingFromDictionary()

  Const sSOURCE As String = "ReadingFromDictionary"

  Dim oPerfMon As CDevPerformanceMonitor
  Set oPerfMon = New CDevPerformanceMonitor
  Dim i As Long
  Dim dcTest
  SetUp

  Set dcTest = CreateObject("Scripting.Dictionary")
  'Set dcTest = New Dictionary
  dcTest.Add "key", "test"

  Dim stTest As String
  Dim dblTimeElapsed As Double

  oPerfMon.StartCounter

  For i = 0 To p_lngTestCount
    stTest = dcTest.Item("key")
  Next i

  dblTimeElapsed = oPerfMon.TimeElapsed

  Debug.Print sSOURCE & ": " & p_lngTestCount & " iterations. " & vbCrLf & _
              "Time elapsed: " & Round(dblTimeElapsed, 0) & "ms" & vbCrLf
End Sub

Sub ReadingFromCollection()

  Const sSOURCE As String = "ReadingFromCollection"

  Dim oPerfMon As CDevPerformanceMonitor
  Set oPerfMon = New CDevPerformanceMonitor
  Dim i As Long
  Dim colTest As Collection
  SetUp

  Dim stTest As String
  Dim dblTimeElapsed As Double
  Set colTest = New Collection
  colTest.Add "test", "key"

  oPerfMon.StartCounter

  For i = 0 To p_lngTestCount
    stTest = colTest.Item("key")
  Next i

  dblTimeElapsed = oPerfMon.TimeElapsed

  Debug.Print sSOURCE & ": " & p_lngTestCount & " iterations. " & vbCrLf & _
              "Time elapsed: " & Round(dblTimeElapsed, 0) & "ms" & vbCrLf
End Sub
Run Code Online (Sandbox Code Playgroud)

性能监视器类(CDevPerformanceMonitor):

Option Explicit

' Performance monitoring used in logging
' See: /sf/ask/13888661/

Private Type LARGE_INTEGER
  lowpart As Long
  highpart As Long
End Type

#If VBA7 Then
  Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
#Else
  Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
#End If

#If VBA7 Then
  Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long
#Else
  Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long
#End If

Private m_CounterStart As LARGE_INTEGER
Private m_CounterEnd As LARGE_INTEGER
Private m_crFrequency As Double

Private Const TWO_32 = 4294967296#               ' = 256# * 256# * 256# * 256#

Private Function LI2Double(LI As LARGE_INTEGER) As Double
  Dim Low As Double
  Low = LI.lowpart
  If Low < 0 Then
    Low = Low + TWO_32
  End If
  LI2Double = LI.highpart * TWO_32 + Low
End Function

Private Sub Class_Initialize()
  Dim PerfFrequency As LARGE_INTEGER
  QueryPerformanceFrequency PerfFrequency
  m_crFrequency = LI2Double(PerfFrequency)
End Sub

Public Sub StartCounter()
  QueryPerformanceCounter m_CounterStart
End Sub

Public Function PerformanceCount() As Double
  Dim liPerformanceCount As LARGE_INTEGER
  QueryPerformanceCounter liPerformanceCount
  PerformanceCount = LI2Double(liPerformanceCount)
End Function

Public Function MicroTime() As Double
  MicroTime = Me.PerformanceCount * 1000000# / m_crFrequency
End Function

Public Property Get TimeElapsed() As Double
  Dim crStart As Double
  Dim crStop As Double
  QueryPerformanceCounter m_CounterEnd
  crStart = LI2Double(m_CounterStart)
  crStop = LI2Double(m_CounterEnd)
  TimeElapsed = 1000# * (crStop - crStart) / m_crFrequency
End Property
Run Code Online (Sandbox Code Playgroud)