CollectionとDictionaryの違いについて解説します。
同じところ
どちらも配列のような「複数のデータをまとめて持っておく仕組み」で、
Key(ID)とItem(値)をペアで格納することができます。
例えば商品コードと商品名をペアで格納&呼び出すコードは、
以下のようにほとんど同じ(引数の順番だけ違う)コードになります。
Sub Collectionに商品コードと商品名のペアを格納する() Dim Clct対象商品 As New Collection Clct対象商品.Add "みかん", "A-1" Clct対象商品.Add "りんご", "A-2" Clct対象商品.Add "いちご", "A-3" Clct対象商品.Add "さくらんぼ", "A-4" Debug.Print Clct対象商品("A-3") ' ←いちご End Sub
Sub Dictionaryに商品コードと商品名のペアを格納する() Dim Dic対象商品 As New Dictionary Dic対象商品.Add "A-1", "みかん" Dic対象商品.Add "A-2", "りんご" Dic対象商品.Add "A-3", "いちご" Dic対象商品.Add "A-4", "さくらんぼ" Debug.Print Dic対象商品("A-3") ' ←いちご End Sub
違うところ
端的には「DictionaryはCollectionの完全上位互換」と捉えてよいです。
細かい違いを以下に解説していきます。
CollectionはKeyを省略可能
まずはじめに、
- CollectionはKeyを持たない単純なItemのリスト
- DictionaryはKeyからItemを取得する仕組み
と勘違いされている方をたまに見かけますが、
CollectionはKeyを省略できるだけで、ちゃんとKeyはあります。
この「CollectionはKeyを省略できる」というポイントから説明すると、
まずCollectionは「KeyとIndexの両方でアクセス可能」という特長があります。
Sub Collectionに商品コードと商品名のペアを格納する() Dim Clct対象商品 As New Collection Clct対象商品.Add "みかん", "A-1" Clct対象商品.Add "りんご", "A-2" Clct対象商品.Add "いちご", "A-3" Clct対象商品.Add "さくらんぼ", "A-4" ' ↓このどちらも「いちご」を取得可能 Debug.Print Clct対象商品("A-3") Debug.Print Clct対象商品(3) End Sub
これは「Worksheets(1)」と「Worksheets("○○")」の両記述が可能という、Worksheetsコレクションでもおなじみの仕様ですね。
このようにCollectionはKeyとIndex両方でアクセスが可能なため、
KeyがなくてもIndexだけで運用することも可能です。
よってCollectionはKeyを省略することができ、
Dim Clct対象商品 As New Collection Clct対象商品.Add "みかん" Clct対象商品.Add "りんご" Clct対象商品.Add "いちご" Clct対象商品.Add "さくらんぼ" Debug.Print Clct対象商品(3) ' ←いちご
このようにIndexのみで運用することが可能です。
Keyを省略可能にするには引数をItemより後ろにする必要があるので、
それでDictionaryとKey-Itemの引数順が逆になっているわけですね。
Collectionはこのコードの方がよく見かけるコードではないでしょうか?
- CollectionはKeyなしでItemのみの運用をすることが可能
というポイントをまずは押さえておいてください。
その上で、本記事の結論を先に述べますと、
- KeyとItemを両方使用する場合はDictionaryが完全上位互換
- CollectionはItemだけで運用できるのが唯一の長所
- しかしDictionaryには「Keyだけで運用する」というテクニックがある
- 「ItemだけCollection」と「KeyだけDictionary」は後者が便利
- よってほぼすべての局面でDictionaryが上位互換と言ってもよい
という流れになります。
詳しい説明を読みたい方は以下読み進めて下さい。
CollectionとDictionaryの機能比較
上記を踏まえ「KeyとItemを両方使用した場合」について両者を比較します。
前述の通りDictionaryが完全上位互換と言っていい性能を誇り、
簡単に一覧するとこのようになります。
- DictionaryはKeyの存在チェックが可能(IsExists)
- このためDictionaryはユニーク(重複なし)リストの生成が容易
- DictionaryはKey、Itemを一次元配列に変換可能
- CollectionはItemの書き換えができない
- CollectionはKeyの一覧を取得することができない
上記3つに並んだDictionaryのメリットは非常に強力で、
ExcelVBAにおける最強の配列と呼んで差し支えないと思います。
対してCollectionは下2つのデメリットが深刻で、
以下のコードが使用できない問題を抱えています。
Dim Clct対象商品 As New Collection Clct対象商品.Add "みかん", "A-1" Clct対象商品.Add "りんご", "A-2" Clct対象商品.Add "いちご", "A-3" Clct対象商品.Add "さくらんぼ", "A-4" ' このコードはエラー。一度入れた値は書き換えできない。 Clct対象商品("A-3") = "苺" ' 加えてCollectionは「A-1」「A-2」「A-3」「A-4」のリストを取得する方法が存在しない。
このように「KeyとItemをペアで管理する仕組み」として使う場合は、
Collectionを使用するメリットはまったくありません。
Key-Itemのペアを管理する場合は常にDictionaryを使用してください。
DictionaryをKeyのみで使用するテクニック
ここまでで
- KeyとItemを両方使用する場合はDictionaryが完全上位互換
- CollectionはItemだけで運用できるのが唯一の長所
という説明をしてきました。
しかしながら、Dictionaryには「Keyだけで使用する」というテクニックがあります。
Sub Dictionaryに商品名を格納する() Dim Dic対象商品 As New Dictionary Dic対象商品.Add "みかん", "" Dic対象商品.Add "りんご", "" Dic対象商品.Add "いちご", "" Dic対象商品.Add "さくらんぼ", "" Dim Arr商品リスト Arr商品リスト = Dic対象商品.Keys End Sub
Itemの省略はできませんので逐一「""」を入れる必要がありますが、
これで「Keyのみの一次元配列」を運用できることがわかります。
この「KeyのみDictionary」と「ItemのみCollection」を比較すると、
- DictionaryはKeyの存在チェックが可能(IsExists)
- このためDictionaryはユニーク(重複なし)リストの生成が容易
- DictionaryはKey、Itemを一次元配列に変換可能
この前述の3点のメリットがそのままKeyのみDicitonaryでも使えますので、
結局「1要素だけの一次元配列」としてもDictionaryが上回る結果になります。
特に「要素の存在チェック」ができる一次元配列がDictionaryだけなので、
Collectionだけでなく、配列(Array)と比較してもDicitonaryは優秀です。
ただし、CollectionのItemは重複を許しますので、
Dim Clct対象商品 As New Collection Clct対象商品.Add "みかん" Clct対象商品.Add "みかん" Clct対象商品.Add "みかん"
これができるというのがDictionaryに勝る唯一のポイントになります。
重複なしリストでは逆に困る(重複も許す必要がある)場合は、
Dictioanryは使えませんのでCollectionを用いて下さい。
使い分け
ここまででまとめた通り、
- KeyとItemを両方使用する場合はDictionaryが完全上位互換
- CollectionはItemだけで運用できるのが唯一の長所
- しかしDictionaryには「Keyだけで運用する」というテクニックがある
- 「ItemだけCollection」と「KeyだけDictionary」はやはり後者が便利
- よってほぼすべての局面でDictionaryが上位互換と言ってもよい
という結論となっていますので、
すべてDictionaryを使用してしまって構いません。
Collectionの使用は、
Dim Clct対象商品 As New Collection Clct対象商品.Add "みかん" Clct対象商品.Add "みかん" Clct対象商品.Add "みかん"
この「重複を許すItemのみの一次元配列」のみと思えばOKです。
ちなみにDictionaryには「Scripting Runtime」の参照設定が必要ですが、
これはFileSystemObjectにも必要なライブラリです。
慣れれば10秒かかりませんので、Dicitonary/FSOの使用にかかわらず、
マクロブックを作成したら初手で全ブック設定してしまっていいと思います。
以上でCollectionとDictionaryの違いの解説を終わります。
CollectionとDictionaryの使い方については解説動画もありますので、
よろしければご視聴ください。
補足説明と注意事項
CollectionはKeyに数値を指定不可
CollectionはKeyとIndex両方でアクセスが可能なため、
当然ながら「数値ならIndexと判定される」仕様で動いています。
このため、CollectionのKeyには数値型を指定することはできず、
以下のコードは「型が一致しません。」エラーとなります。
Clct対象商品.Add "みかん", 1 ' ←Keyに数値はNG
数値を無理やり文字列にすれば対応することはでき、
Clct対象商品.Add "みかん", "1" ' ←これでOK
このコードは実行できます。
Worksheets(1)とWorksheets("1")の違いと同じですね。
対してDictionaryはIndexが存在しませんので、
Keyにも問題なく数値を指定することが可能です。
Dic対象商品.Add 1, "みかん"
記事の結論が「Key-Itemを使うならDictionary一択」でしたので補足に回しましたが、
もし使用する場合はこの仕様を覚えておいてください。
CollectionはAddの挿入位置を指定可能
かなりレアケースのため本編では説明を省きましたが、
CollectionのAddメソッドには「Before/After」を指定できます。
みかんといちごの間にぶどうを挿し込むことができるわけですね。
よってカードのシャッフルやキャラの行動順など、
ゲームの実装ではこの仕様が必要になることがあります。
なかなかExcelの事務仕事でこれが必要になる場面は少なそうですが、
もし必要になったらこの仕様を思い出してください。
CollectionはIndexでループすると遅い
CollectionはIndexでアクセスが可能としましたが、
全要素を取り出す場合はIndexを使用しない方がよいです。
CollectionはIndexでアクセスする場合非常に遅くなる特徴があり、
For i = 1 To コレクション.Count For Each 要素 In コレクション
この二つはかなりの速度差があります。(下が高速)
「Indexがある」というのがCollectionの特長だったのに、何とも悲しい仕様ですね。
Collectionの全要素をループしたい場合は、原則For Each文を使用してください。
DictionaryはIndexを持たない
DictionaryはIndexを持たないとここまで述べてきましたが、
「あれ?Dic.Keys(i)って書けるけど。。。」
と思われた方もいらっしゃるかもしれません。
実はこの記述はあまり推奨できない記述であり、
使い方によってエラーや大幅な速度低下を招く可能性があります。
Keys(i)やItems(i)という記述をしてしまっている方は、
以下の記事をお読みいただき、必要に応じて修正を行ってください。
詳しくはこちらをどうぞ。
www.limecode.jp