VBAを覚えたての頃、僕はデータを溜め込む処理を全部配列で書いていた。
配列で困ることはほとんどなかったが、部署別の売上を集計するようなコードを書いたときに、様子が変わった。
配列の中を毎回ループで検索して「この部署、もう登録済みかどうか」を確認するコードになり、データ件数が増えるほど処理がもたつくようになった。
そこで知ったのがDictionary。キーで直接呼び出せるので、検索のためのループがまるごと不要になる。
この記事では、配列の宣言・初期化からDictionaryの使い方まで、「結局どっちを使えばいいのか」という判断軸を軸にまとめました。

配列とDictionary、何が違うのか
配列は、0番・1番・2番…というインデックス(連番)でアクセスする箱。
一方Dictionaryは、”東京”や”田中”のようなキーでアクセスするラベル付きの引き出しと考えると分かりやすい。
両者の違いを表にすると、こうなる。
| 項目 | 配列 | Dictionary |
|---|---|---|
| アクセス方法 | インデックス(0, 1, 2…) | キー(文字列・数値) |
| 要素の順序 | 宣言順に保持される | 追加順に近いが仕様上は保証されない |
| キーの重複 | インデックスなので概念自体がない | Addで重複させるとエラーになる |
| 主な用途 | 一時的なデータ保持、順序が意味を持つ処理 | 集計、重複チェック、コード→名前の変換 |
| 事前準備 | 不要 | 参照設定 or CreateObjectの選択が必要 |
配列とDictionary、どっちを使うべきか
僕が実務で判断に使っている基準はシンプルで、「パフォーマンスを意識したい」「キーから直接呼び出したい」「可読性を上げたい」のいずれかに当てはまればDictionary。
それ以外の、単純にデータを一時保存するだけの処理は配列で十分。
「パフォーマンスを意識したい」というのは、具体的には検索や重複チェックが絡む処理のこと。
配列であるキーが登録済みかを調べるには、先頭から順にループで比較するしかない(データがn件ならn回のループ)。
Dictionaryはハッシュテーブルという仕組みでキーを管理しているため、件数が増えてもほぼ一定の速さで検索できる。
件数が少ないうちは体感しにくいが、数千行を超えるデータを扱うマクロでは差がはっきり出てくる。
配列の基本を押さえる
固定長配列:サイズが決まっている処理に
固定長配列は、宣言時に要素数を指定するタイプ。サイズが変わらない分、シンプルで高速に動く。
Sub DeclareFixedArray()
Dim scores(0 To 4) As Integer ' 5つの要素を持つ整数型配列
scores(0) = 80
scores(1) = 90
scores(2) = 85
End SubDim 配列名(開始Index To 終了Index)で宣言する。特に指定しなければ0始まりになる。
動的配列:サイズが読めない処理に
データ件数が実行してみないと分からない場合は、動的配列を使う。
Sub DeclareDynamicArray()
Dim names() As String
ReDim names(3) ' 要素数4(0〜3)
names(0) = "Taro"
names(1) = "Hanako"
End SubDim 配列名()で型だけ宣言し、あとからReDimでサイズを決める流れ。
ReDim Preserveの罠
動的配列を覚えたての頃、一番ハマったのがここ。ReDimだけを使うと、それまでの配列の中身が消える。
ループの途中でうっかり素のReDimを書いてしまい、集計結果が全部消えて青ざめたことがある。
中身を保持したままサイズだけ変えたいときは、ReDim Preserveを使う。
Sub ExtendArrayPreserve()
Dim nums() As Long
ReDim nums(2)
nums(0) = 1
nums(1) = 2
ReDim Preserve nums(4) ' 既存の値を保持したまま要素数を拡張
nums(3) = 3
nums(4) = 4
End Sub変更できるのは最終次元のみという制約もある。多次元配列でPreserveを使うときは要注意。
多次元配列:行×列のデータに
表形式のデータを扱いたいときは、多次元配列が便利。Excelのセルのようなイメージで管理できる。
Sub Declare2DArray()
Dim table(1 To 3, 1 To 2) As String
table(1, 1) = "A"
table(1, 2) = "B"
table(2, 1) = "C"
End SubDim 配列名(行の範囲, 列の範囲)で宣言する。行と列のインデックスをそれぞれ個別に指定できる。
配列の要素数を取得する
VBAには配列の要素数を直接返す関数がない。UBound(最大インデックス)とLBound(最小インデックス)の差分で算出する。
Sub CountFixedArray()
Dim fruits(1 To 5) As String
Dim cnt As Long
cnt = UBound(fruits) - LBound(fruits) + 1
MsgBox "要素数は " & cnt & " です"
End Sub+1を忘れがちなので要注意。インデックスの差だけだと1つ少なく数えてしまう。
また、動的配列はReDimでサイズを指定するまでは未初期化の状態。このままUBoundを使うとエラーになる点も覚えておきたい。
二次元配列では、第2引数に1(行方向)・2(列方向)を指定すれば、各次元の要素数を個別に取得できる。
Sub Count2DArray()
Dim matrix(1 To 3, 1 To 4) As Integer
Dim rowCount As Long, colCount As Long
rowCount = UBound(matrix, 1) - LBound(matrix, 1) + 1
colCount = UBound(matrix, 2) - LBound(matrix, 2) + 1
MsgBox "行数: " & rowCount & ", 列数: " & colCount
End SubDictionaryを使う準備をする
Dictionaryは配列と違い、使い始める前にひと手間必要になる。方法は2つある。
早期バインディング(参照設定)
VBEの「ツール」→「参照設定」から「Microsoft Scripting Runtime」にチェックを入れる方法。
Dim dict As New Dictionary ' 参照設定が前提
dict.Add "東京", 100入力補完(インテリセンス)が効くのがメリット。ただし、このマクロを別のPCに配布すると、相手の環境で参照設定が外れていて動かないことがある。
実行時バインディング(CreateObject)
参照設定をせず、CreateObjectでDictionaryを生成する方法。
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "東京", 100参照設定なしで動くので、配布先のPC環境に左右されないのが強み。入力補完は効かなくなるが、実務で使うマクロは基本的にこちらにしている。
個人のツールとして手元だけで動かすなら早期バインディング、他の人に配るマクロなら実行時バインディング、というのが僕の使い分け。
Dictionaryの基本操作
基本のメソッドは7つ覚えておけば困らない。
Sub DictionaryBasicMethods()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "東京", 100 ' キーと値を追加
dict.Add "大阪", 80
dict.Add "福岡", 50
If dict.Exists("東京") Then ' キーの存在確認
Debug.Print dict.Item("東京") ' 値の取得(Itemは省略可)
End If
Dim k As Variant
For Each k In dict.Keys ' キー一覧の取得
Debug.Print k & ": " & dict(k)
Next k
Debug.Print "件数: " & dict.Count ' 登録件数
dict.Remove "福岡" ' 削除
End Sub- Add:キーと値を追加
- Exists:キーが登録済みか確認
- Item:値を取得・更新(
dict("キー")と省略できる) - Keys:登録済みキーの一覧を配列で取得
- Items:登録済み値の一覧を配列で取得
- Count:登録件数を取得
- Remove:指定したキーを削除
Dictionaryでよくあるエラーと対処
配列と同じくらい、僕が最初につまずいたのがキーの重複エラー。同じキーでAddを2回実行すると、実行時エラーになる。
Sub DuplicateKeyError()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "東京", 100
dict.Add "東京", 200 ' ← 実行時エラー(キーは既に存在します)
End Sub集計処理では「同じキーが2回目に出てきたら加算する」という場面が頻繁にある。そのたびにExistsで確認してから、追加か更新かを分岐させるのが安全な書き方。
Sub AvoidDuplicateKeyError()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "東京", 100
If dict.Exists("東京") Then
dict("東京") = dict("東京") + 200 ' 既存キーは加算して更新
Else
dict.Add "東京", 200 ' 新規キーは追加
End If
End SubちなみにItemへの代入(dict("東京") = 値)は、キーが存在しなければ自動的に追加、存在すれば上書きになる。
この挙動を知らずにAddだけで書き続けると、同じ重複エラーに何度も当たることになる。地味に見落としがちなポイント。
実務での使い分けを体感する:部署別集計
最後に、配列だと重くなりがちで、Dictionaryだと素直に書ける典型例を紹介する。
A列に部署名、B列に金額が並んだ表から、部署ごとの合計をD列・E列に書き出す処理。
Sub SumByDepartment()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
Dim i As Long, dept As String, amount As Long
For i = 2 To lastRow
dept = Cells(i, 1).Value
amount = Cells(i, 2).Value
If dict.Exists(dept) Then
dict(dept) = dict(dept) + amount
Else
dict.Add dept, amount
End If
Next i
Dim k As Variant, r As Long
r = 1
For Each k In dict.Keys
Cells(r, 4).Value = k
Cells(r, 5).Value = dict(k)
r = r + 1
Next k
End Subこれを配列だけで書こうとすると、「この部署は登録済みか」を確認するために、毎行ごとに配列全体をループで検索する処理が必要になる。
部署数が少なければ気にならないが、キーの候補が数百件を超えると、この検索コストが効いてくる。
dict.Exists(dept)の一行で済むDictionaryのほうが、コードの見通しも処理速度も良くなる場面。
よくある質問
本文で触れきれなかった細かい疑問をQ&Aでまとめておきます。
固定長配列はReDimでサイズ変更できますか?
できません。固定長配列(Dim arr(5) As Stringのように宣言したもの)に対してReDimを使うとコンパイルエラーになります。サイズ変更が必要な場合は、最初から動的配列(Dim arr() As String)として宣言してください。
Dictionaryのキーに数値は使えますか?
使えます。キーは文字列だけでなく、数値や日付などのデータ型を指定できます。ただし内部的には型を区別するため、キーとして1(数値)と"1"(文字列)は別のキーとして扱われる点には注意してください。
Dictionaryは配列より必ず速いのですか?
データ件数が少ない、または単純に先頭から順番に処理するだけの場合は、配列とDictionaryの速度差はほとんど体感できません。Dictionaryが優位になるのは、キーの存在確認や検索を繰り返す処理です。用途に合わない場面でDictionary化しても、コードが複雑になるだけのこともあります。
二次元配列のループはどう書けばいいですか?
行と列それぞれに対してLBound(arr, 次元)からUBound(arr, 次元)までのForループをネストさせます。1次元目が行、2次元目が列に対応するのが一般的な書き方です。
まとめ
配列は順序を持つ一時的なデータ保存に、Dictionaryはキーからの検索・集計・重複チェックに向いている。
迷ったら「パフォーマンス」「キー参照」「可読性」のどれかが当てはまるかを考えれば、自然とどちらを使うべきかが見えてくるはずだ。

コメント