vba 字典、集合和数组的比较
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32479842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Comparison of Dictionary, Collections and Arrays
提问by HarveyFrench
I am trying to work out the relative benefits and features of dictionaries compared with collections and arrays.
我试图找出字典与集合和数组相比的相对优势和特征。
I found an excellent article herebut can't find a simple table that compares all the various features.
我在这里找到了一篇很棒的文章,但找不到一个简单的表格来比较所有不同的功能。
Does anyone know of one?
有人知道吗?
回答by HarveyFrench
Please see the table below for a useful comparison of collections and dictionaries.
请参阅下表以对集合和字典进行有用的比较。
(The table summarises this pageup to the section on "Early And late binding". FYI the page also has more detailed info about using dictionaries)
(表格总结了这个页面直到“早期和晚期绑定”部分。仅供参考,该页面还包含有关使用词典的更多详细信息)
In summary it's usually best to use a dictionary or an array.
总之,通常最好使用字典或数组。
When considering using collections it may be more appropriate to use an array if the size does not change, or changes only rarely. In this case an array is likely to be more efficient than a collection as Arrays?are very efficient to populate and retrieve all items at once (eg. range to array and array back to range).
在考虑使用集合时,如果大小不变或很少变化,则使用数组可能更合适。在这种情况下,数组可能比集合更有效,因为数组可以非常有效地一次填充和检索所有项目(例如,范围到数组和数组返回到范围)。
Also note:
另请注意:
Compared to Arrays, collections offer good performance for adding and inserting items, and accessing and removing them by their Keys. However, performance is poor if items are to be accessed by index. For information about doing this efficiently see herewhich also discusses the inner workings of these list objects.
与数组相比,集合在添加和插入项目以及通过其键访问和删除项目方面提供了良好的性能。但是,如果要通过索引访问项目,则性能很差。有关有效执行此操作的信息,请参阅此处,其中还讨论了这些列表对象的内部工作原理。
This cpearson pagehas has very useful code for working with dictionaries, collections and arrays (sorting them, and also converting them to be each other!)
这个 cpearson 页面有非常有用的代码,用于处理字典、集合和数组(对它们进行排序,并将它们相互转换!)
Some text from cpearson's page:
cpearson 页面上的一些文字:
The Collection object and the Dictionary object are very useful for storing groups of related data. All else being equal, I use a Dictionary object rather than a Collection object because you have access (read, write, change) to the Key property associated with an Item in the Dictionary. In a rather poor object design, the Key of an item in a Collection is write-only. You can assign a Key to an Item when you add the Item to the Collection, but you cannot retrieve the Key associated with an Item nor can you determine (directly) whether a key exists in a Collection. Dictionaries are much friendly and open with their keys. Dictionaries are also considerably faster than Collections.
Collection 对象和 Dictionary 对象对于存储相关数据组非常有用。在其他条件相同的情况下,我使用 Dictionary 对象而不是 Collection 对象,因为您可以访问(读取、写入、更改)与 Dictionary 中的项关联的 Key 属性。在相当糟糕的对象设计中,集合中项目的键是只写的。您可以在将项添加到集合时为项分配键,但您无法检索与项关联的键,也无法(直接)确定键是否存在于集合中。字典非常友好并且可以用钥匙打开。字典也比集合快得多。
Why can arrays be a bad choice. Arrays are much slower at re-sizing and inserting items in the middle as each Redim copies the entire memory block to a larger location, and if Preserve is used, all values copied over as well. This may translate to perceived slowness for every operation - in a potential application)
为什么数组可能是一个糟糕的选择。数组在重新调整大小和在中间插入项目时要慢得多,因为每个 Redim 将整个内存块复制到更大的位置,如果使用 Preserve,所有值也会被复制。这可能会转化为每个操作的感知缓慢 - 在潜在的应用程序中)
Collections vs. Dictionaries in VBA
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!
回答by paul bica
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
回答by neilt17
With regards to the performance of collections versus dictionaries, I am finding that writing to dictionaries performs similarly to writing to collections, reading from a dictionary takes about twice as long as reading from a collection. Creating a dictionary in the first place is way slower than creating a collection.
关于集合与字典的性能,我发现写入字典的性能与写入集合的性能相似,从字典中读取的时间大约是从集合中读取的时间的两倍。首先创建字典比创建集合慢。
These are results I got for doing 100,000 iterations of reading from, writing to, and creating dictionaries/collections:
这些是我对字典/集合进行 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
Note adding a reference to the Microsoft Scripting Runtine improves the speed of creating multiple dictionaries (to 495ms here).
请注意,添加对 Microsoft Scripting Runtine 的引用可提高创建多个字典的速度(此处为 495 毫秒)。
This is the code I used for testing this:
这是我用来测试的代码:
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
Performance monitor class (CDevPerformanceMonitor):
性能监视器类(CDevPerformanceMonitor):
Option Explicit
' Performance monitoring used in logging
' See: https://stackoverflow.com/questions/198409/how-do-you-test-running-time-of-vba-code
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
回答by rishi
Option Explicit
Sub UpdateSummary()
Dim varData
Dim objDicCountry As Object
Dim objDicCity As Object
Dim objDicData As Object
Dim lngR As Long
Dim varResult
Dim lngC As Long
Dim strKey As String
Dim varUnique
varData = Sheet12.Range("A1").CurrentRegion
Set objDicCity = CreateObject("Scripting.Dictionary")
Set objDicCountry = CreateObject("Scripting.Dictionary")
Set objDicData = CreateObject("Scripting.Dictionary")
For lngR = LBound(varData) + 1 To UBound(varData)
strKey = varData(lngR, 1) '--Country
objDicCountry.Item(strKey) = ""
strKey = varData(lngR, 2) '--City
objDicCity.Item(strKey) = ""
strKey = varData(lngR, 1) & "|" & varData(lngR, 2) '--Country and City
objDicData.Item(strKey) = objDicData.Item(strKey) + varData(lngR, 3)
Next lngR
ReDim varResult(1 To objDicCountry.Count + 1, 1 To objDicCity.Count + 1)
varUnique = objDicCountry.keys '--get Unique Country
For lngR = LBound(varUnique) To UBound(varUnique)
varResult(lngR + 2, 1) = varUnique(lngR)
Next lngR
varUnique = objDicCity.keys '--get Unique City
For lngC = LBound(varUnique) To UBound(varUnique)
varResult(1, lngC + 2) = varUnique(lngC)
Next lngC
For lngR = LBound(varResult) + 1 To UBound(varResult)
For lngC = LBound(varResult) + 1 To UBound(varResult, 2)
strKey = varResult(lngR, 1) & "|" & varResult(1, lngC) '--Country & "|" & City
varResult(lngR, lngC) = objDicData.Item(strKey)
Next lngC
Next lngR
Sheet12.Range("F6").Resize(UBound(varResult), UBound(varResult, 2)).Value = varResult
MsgBox "Done", vbInformation
End Sub

