web-dev-qa-db-ja.com

辞書、コレクション、配列の比較

コレクションと配列と比較した辞書の相対的な利点と機能を解決しようとしています。

素晴らしい記事を見つけました こちら ですが、さまざまな機能をすべて比較した簡単な表は見つかりません。

誰か知っていますか?

18
HarveyFrench

コレクションと辞書の便利な比較については、以下の表をご覧ください。

(表は このページ 「早期バインディングと遅延バインディング」のセクションまでをまとめたものです。参考までに、このページには辞書の使用に関する詳細情報もあります)

要約すると、通常は辞書または配列を使用するのが最善です。

コレクションの使用を検討する場合、サイズが変更されないか、まれにしか変更されない場合、配列を使用する方が適切な場合があります。この場合、配列はコレクションよりも効率的である可能性が高いのは、配列がすべてのアイテムを一度に入力および取得するのに非常に効率的であるためです(たとえば、範囲から配列、および配列から範囲に戻る)。

また注意してください:

配列と比較して、コレクションは、アイテムの追加と挿入、およびキーによるアイテムへのアクセスと削除に関して優れたパフォーマンスを提供します。ただし、インデックスによってアイテムにアクセスする場合、パフォーマンスは低下します。これを効率的に行う方法については、 here を参照してください。これらのリストオブジェクトの内部動作についても説明しています。

このcpearsonページ には、辞書、コレクション、配列を操作するための非常に便利なコードがあります(それらを並べ替え、相互に変換します!)

Cpearsonのページのテキスト:

CollectionオブジェクトとDictionaryオブジェクトは、関連データのグループを保存するのに非常に役立ちます。他のすべてが等しい場合、辞書内のアイテムに関連付けられた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!
39
HarveyFrench
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
16
paul bica
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
0
rishi

コレクションと辞書のパフォーマンスに関しては、辞書への書き込みはコレクションへの書き込みと同様に実行され、辞書からの読み取りにはコレクションからの読み取りの約2倍の時間がかかることがわかりました。そもそも辞書を作成するのは、コレクションを作成するよりもずっと遅いです。

これらは、辞書/コレクションの読み取り、書き込み、および辞書の作成を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

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

パフォーマンスモニタークラス(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
0
neilt17