VBA

DictionaryオブジェクトとCollectionオブジェクトどっち使う?違いとメリットデメリット、そして使いどころ

記事内に商品プロモーションを含む場合があります

このページでわかること

DictionaryとCollectionの違い(特にAddメソッドと引数Key)についてわかります。
DictionaryとCollectionのメリットデメリットを分析し、どちらを使うべきかがわかります。

覚えること
  1. Dictionaryオブジェクト
    引数はKey,Itemの順番
    Key,Itemどちらも必須
    Keyには何でも代入できる(オブジェクトも!!)
  2. Collectionオブジェクト
    引数はItem,Keyの順番
    Keyは省略可
    Keyには数値、日付は代入できない。文字列はOK!!
  3. 速度はKeyに代入しなければCollectionオブジェクトの方が速い
    ただし、そこまで気にしなくてもいい。どっちも速い!
  4. どちらを使用するべきか
    リスト作成であればDictionaryオブジェクトが無難。重複ありであればどちらでもいい。
    Keyを使った連想配列を使用する場合はDiciotnaryの方が便利。
    Keyを使うかどうかが判断基準となる

こんにちは、hokkyokunです。
みなさんはDictionaryオブジェクトとCollectionオブジェクトどっち使ってますか?

似たようなオブジェクトですが、
実務的には大きく違う点があります。

整理していきましょう。

CollectionとDictionaryのAddメソッド(追加方法)は違う

構文を整理しましょう。

Collectionオブジェクト
Collectionオブジェクト.Add( Item , [Key] , [Before] , [After])
Dictionaryオブジェクト
Dictionaryオブジェクト.Add(Key , Item)

違いをまとめてみました。
ここが一番大事なところです

Dictionaryオブジェクト

  • 引数はKey,Itemの順番
  • Key,Itemどちらも必須
  • Keyには何でも代入できる(オブジェクトも!!)
  • Collectionオブジェクト

  • 引数はItem,Keyの順番
  • Keyは省略可
  • Keyには数値、日付は代入できない。文字列はOK!!
  • Collectionオブジェクトは引数Keyの取扱に注意

    Keyに数値を入れるとエラー

    Collectionオブジェクトの引数Keyに数値を入力するとエラーが生じます。
    実験してみましょう

    下記のような表を作ります。
    番号(数値)をKeyに、果物(文字列)をItemに入れてみます。

    Sub CollectionのKeyに数値を代入()
    
    Dim Rng As Range 'Collectionに取り込む変数
    Dim r As Range 'For eachで使う変数
    Dim Coll As New Collection 'Collectionのインスタンス
    
    'Collectionに取り込む範囲をセット
    Set Rng = ActiveSheet.Range("A2:A5")
    
    'Itemに文字列(果物)、Keyに数値(番号)を入れてみる
    For Each r In Rng
        Coll.Add Item:=r.Offset(, 1).Value, Key:=r.Value
    Next
    
    End Sub
    

    エラーが発生しました。

    Keyに文字列ならエラーでない

    上記と逆に
    果物(文字列)をKeyに、番号(数値)をItemに代入してみます。

    Sub CollectionのKeyに文字列を代入()
    
    Dim Rng As Range 'Collectionに取り込む変数
    Dim r As Range 'For eachで使う変数
    Dim Coll As New Collection 'Collectionのインスタンス
    Dim myKey As String 'Collectionに取り込むKey
    Dim msg As String 'メッセージボックス用変数
    
    'Collectionに取り込む範囲をセット
    Set Rng = ActiveSheet.Range("A2:A5")
    
    'Itemに数値(番号)、Keyに文字列(果物)を入れてみる
    For Each r In Rng
        myKey = r.Offset(, 1).Value
        Coll.Add Item:=r.Value, Key:=myKey
        
        '取り込むKeyとItemをメッセージボックス用の変数に代入
        msg = msg & vbCrLf & "Key:Item=" & myKey & ":" & Coll(myKey)
    Next
    
    MsgBox msg
    
    End Sub

    うまく処理できました

    CollectionオブジェクトはItemを取り出すためのツール

    Collectionオブジェクトに数値を入れることは出来ませんでした。

    Keyは省略するとインデックス番号を1から入れていくことになるので、
    勝手に数値を入れることが出来ない仕様になっているんだと思います。

    また、CollectionオブジェクトからKeyを取り出すメソッドはありませんでした。

    CollectionオブジェクトはKeyを取り出してどうこうすることを想定しておらず、
    あくまでItemを取り出すためのツールのようです。

    Dictionaryは何でもOK

    Dictionaryは自由度高しです。
    Keyに何を入れてもいいし、Keyを取り出すことも可能です。
    上記の例で処理して見ましょう。

    Sub DictionaryのKeyに数値を代入()
    
    Dim Rng As Range 'Dictionaryに取り込む範囲の変数
    Dim r As Range 'For eachで使う変数
    Dim Dic As New dictionary 'Dictionaryのインスタンス
    Dim msg As String 'メッセージボックス用変数
    Dim i As Long
    
    'Dictionaryに取り込む範囲をセット
    Set Rng = ActiveSheet.Range("A2:A5")
    
    'Keyに数値(番号)、Itemに文字列(果物)を入れてみる
    For Each r In Rng
        Dic.Add Key:=r.Value, Item:=r.Offset(, 1).Value
        msg = msg & vbCrLf & "Key:Item=" & Dic.Keys(i) & ":" & Dic.Item(Dic.Keys(i))
        i = i + 1
    Next
    
    MsgBox msg
    
    End Sub

    Keyに数値を入れてみましたが、大丈夫でした。

    速度はどっちが速い?

    速度を測定してみました。
    条件としては

    1000行分の文字列(リンゴ1~リンゴ1000)を

    • CollectionはItemに取り込み
    • DictionaryはKeyとItemに取り込み

    これを500回繰り返したときにかかった時間を計測しました。

    Collectionの速度

    Sub 速度測定Collection()
    
    Dim Rng As Range '取り込む範囲の変数
    Dim r As Range 'For each用の変数
    Dim Coll As Collection 'Collectionのインスタンス
    Dim startTime As Date '開始時間の変数
    Dim endTime As Date '終了時間の変数
    Dim myTime As Date '終了時間-開始時間
    Dim i As Long '繰り返し用の変数
    
    '開始
    startTime = Time
    
    '取り込む範囲
    Set Rng = ActiveSheet.Range("A2:A1001")
    
    'Itemに1000行取り込み×500回
    For i = 1 To 5000
    Set Coll = New Collection
        For Each r In Rng
            Coll.Add r.Value
        Next
    Next i
    
    '終了
    endTime = Time
    
    'かかった時間
    myTime = Format(endTime - startTime, "hh:nn:ss")
    
    
    MsgBox myTime
    
    End Sub

    Collectionは15秒でした。

    Dictionaryの速度

    Sub 速度測定Dictionary()
    
    Dim Rng As Range '取り込む範囲の変数
    Dim r As Range 'For each用の変数
    Dim Dic As dictionary 'Dictionaryのインスタンス
    Dim startTime As Date '開始時間の変数
    Dim endTime As Date '終了時間の変数
    Dim myTime As Date '終了時間-開始時間
    Dim i As Long '繰り返し用の変数
    
    '開始
    startTime = Time
    
    '取り込む範囲
    Set Rng = ActiveSheet.Range("A2:A1001")
    
    'KeyとItemに1000行取り込み×500回
    For i = 1 To 5000
    Set Dic = New dictionary
        For Each r In Rng
            Dic.Add r.Value, r.Value
        Next
    Next i
    
    '終了
    endTime = Time
    
    'かかった時間
    myTime = Format(endTime - startTime, "hh:nn:ss")
    
    
    MsgBox myTime
    
    End Sub

    Dictionaryは31秒でした。

    DictionaryよりCollectionのほうが速い?

    CollectionはDictionaryの半分程度の時間で処理できました。
    Collectionの方が速いということでしょうか?

    厳密に言うと違います。
    CollectionはItemにしか取り込み作業をしていないのに対し、DictionaryはKeyとItem両方に取り込んでいます。
    これが原因でCollectionは早く処理が終わっていると思われます。

    この証拠に、ColletionでKeyにも取り込みをやってみましたが、
    時間は49秒でした(Dictionaryよりも遅いという結果です)。

    じゃあ、Dictionaryのほうが速いのかというと
    これも違うと思います(どっちやねん)。

    keyを使わないでItemにしか取り込み作業をしないということも想定されます。
    例えば、重複ありのリスト作成なんかはkeyは使わなくても処理できます。

    いいたいことは
    目的次第でどちらが適切か変わるということです。

    ただし、どちらも超高速ですので、あまりこの処理で待つということはないかもしれません。

    Collectionオブジェクトは要素の順番を変えられる

    上記の通り、CollectionオブジェクトにはAddメソッドに引数が多く用意されています。
    BeforeとAfterですね。
    これらをうまく使うことでAddした順番にかかわらず、要素の順番をある程度編集することができます。

    方法は引数BeforeもしくはAfterに何番目の前、もしくは後ろに入れるか指定してあげます。

    具体的にみてみましょう。
    リンゴ、バナナという文字列をItemに取り込んでみます。
    当然1番目にリンゴ、2番目にバナナが入ります。
    最後にキウイを取り込む際、一番目の要素にしたいとします。
    Collectionオブジェクト.Add Item:=”キウイ”,Before:=1
    として指定してあげます。

    Sub 順番()
    
    Dim Coll As New Collection
    
    Coll.Add Item:="リンゴ"
    Coll.Add Item:="バナナ"
    Coll.Add Item:="キウイ", Before:=1
    
    End Sub

    このように1番目にリンゴ、2番目にバナナが入ります。

    1番目にキウイ、2番目にリンゴ、3番目にバナナという感じで
    順番を編集できました。

    DictionaryとCollectionのメリットデメリット

    メリットデメリットをまとめてみました。

    Dictionaryオブジェクトのメリット

  • メソッドが多く、汎用性が高い
  • Keyを取りだすのが簡単
  • Keyに何でも入れることが出来る
  • Dictionaryオブジェクトのデメリット

  • Key、Itemどちらにも値を代入しないといけない。
  • メソッドが複雑
  • 重複ありのリストを作るだけならCollectionオブジェクトの方が速い
  • Collectionオブジェクトのメリット

  • メソッドが少なく、簡単
  • Keyへの取り込みは省略可!
  • 重複ありのリスト作成はDiciotnaryより速い
  • 要素の順番をある程度編集できる
  • Collectionオブジェクトのデメリット

  • Keyを取り出すメソッドがない
  • Keyに数値、日付が入れられない
  • Keyにも代入するとDictionaryオブジェクトより遅い
  • お勧めの使い方

    考えるポイントはKeyを使う必要があるかどうか
    だと思います。

    • 使う→Dictionary
    • 使わない→Collection

    という感じです。

    リスト作成はDictionaryが無難(重複ありならCollectionでも大丈夫)

    リスト作成に関して言えばDicitonaryが無難です。
    というか、これ一択でいった方がいいです。

    Collectionでは
    Keyには文字列しか入らないので、
    数値や日付の重複なしリストは作れません。

    重複ありのリストであればCollectionでも使えますし、
    Dictionaryより早く処理できると思われます。

    ただ、使い分けるメリットはさほどないので、
    Dictionaryを使ってリスト作った方が何かと都合がよいと思います。

    Key、Itemの関係で連想配列を使うならDictionaryオブジェクト

    CollectionオブジェクトはどうしてもKeyの扱いが限定的なので、
    やりたいことが出来ない可能性があります。
    また、速度も決して速くはないので、Dictionaryオブジェクトを使うほうがいいかもしれません。

    ただ、エクセルのようなスプレッドシートがすでに連想配列のようになっているので、
    例えばA列の値を検索して、その右(B列)の値を取り出すということをやれば無理して連想配列を使う必要もなくなります。

    速度やコード量の問題もあるので、覚えなくていいわけではないですが、
    優先して学習することではないとは思います。

    Collectionは要素の編集ができることが役に立つ?

    かなりニッチな需要ですが、
    後からAddで追加した要素を先頭に持ってきたいとか、
    そういう処理が必要な場合はCollectionを使ってもいいかもしれません。

    お勧めはDictionaryを学習しましょう。

    知っておくことは大事なのでCollectionを勉強しなくていいわけではないですが、
    Dictionaryを先ず学習し、
    なれてきたらCollectionを使って
    状況により(Keyを使うかどうか、リストは重複ありかなしか)使い分けするとよいと思います。

    Dictionaryはお気に入りのオブジェクトですので、使ってみてください。

    ではでは