🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の Collection — 順序を保ち、伸びるリスト(そしてなぜ Dictionary ではないのか)

|

Excel VBA の Collection — 順序を保ち、伸びるリスト(そしてなぜ Dictionary ではないのか)

TL;DRCollection は、.Add で伸ばし .Remove で縮める、組み込みの 順序付きリストです — ReDim も、事前のサイズ当てもなし。個数の分からない 要素を順にため込み、キーで引く必要がないときに使います。正直に保つ 4 つの ルール:1 始まりcoll(1) が最初の要素)、要素を上書きできないAdd / Remove だけ)、重複キーはエラー 457 を投げ、.Exists が ない — それがあれば Dictionary になっていたであろうもの。

Dim jobs As Collection
Set jobs = New Collection        ' Set + New:Collection はオブジェクト

jobs.Add "Export report"         ' 必要に応じて伸びる — サイズ宣言は不要
jobs.Add "Email the team"
jobs.Add "Archive last month"

Debug.Print jobs.Count           ' -> 3
Debug.Print jobs(1)              ' -> Export report   (1 始まり!)

For Each task In jobs            ' 追加した順にたどる
    Debug.Print task
Next task

集める数が事前に分からないとき — 検証に失敗したすべての行、出会った一意な顧客 ひとりずつ、フォルダー内のすべてのファイル — 配列は扱いにくいものです。サイズを 当て推量し、進みながら ReDim Preserve しなければなりません。Collection は VBA の答えです:空で始まり、.Add 1 回ごとに、追加した順に伸びていくオブジェクト です。この言語で最も便利なオブジェクトの 1 つであり、同時に最も静かに誤用される ものの 1 つでもあります。配列のように見えて、配列とも Dictionary とも違う振る 舞いをするからです。

この記事で学べること

  • 考え方の軸 — Collection はインデックス付きの配列ではなく、順序付きのリストであること
  • なぜ 1 始まりなのか、そしてその「端 1 つ」バグがどう潜むのか
  • 上書きの罠:coll(2) = "x" は編集ではなく — エラーになること
  • キーの仕組み、重複がエラー 457を投げる理由、そして欠けている .Exists
  • 決め方のルール:CollectionArrayDictionary

考え方の軸:番号付きの棚ではなく、育てていく順序付きリスト

配列は番号付きの棚です:Dim a(1 To 100) と宣言すれば、すべての区画が最初 から存在し、区画 i に直接届きます。Collection は、あなたが足し続ける カードの山です:空で始まり、.Add のたびに新しいカードを順に山に落とし、 .Count は進むにつれて増えます。n 枚目のカードを読むことも、1 枚抜き出すことも できますが、「区画 5 に代入する」ことはできません — 固定された区画 5 などなく、 あるのは山の中に今ある 5 枚目のカードだけです。

この 1 つの違いが、以下のすべてのルールを説明します。固定された棚ではなく伸びて いくリストなので、位置は安定したアドレスではありません — それはただその要素の 現在の並び順であり、その場所は要素を足したり取り除いたりすると動きます。

最初のバグを生むルール:Collection は 1 始まり

VBA の配列は既定で 0 始まりです(a(0) が最初の要素)。Collection1 始まりです。最初の要素は coll(1) で、coll(0) は**「プロシージャの 呼び出し、または引数が不正です (エラー 5)」**を投げます。

Dim c As Collection
Set c = New Collection
c.Add "first"
c.Add "second"

Debug.Print c(1)        ' -> first
Debug.Print c(0)        ' -> 実行時エラー 5

これが最も痛いのは、1 つのマクロで両方を混ぜたときです — 同じループの中で配列を arr(0) で、コレクションを coll(1) で読めば、1 つずれるバグはほぼ確実です。 ルールは覚えるだけです:配列は 0 から、Collection は 1 から。 Collection を インデックスでループするときは、いつも For i = 1 To coll.Count です。

配列に慣れた人を驚かせるルール:要素は上書きできない

配列なら a(2) = "new" は区画 2 の値を置き換えます。Collection では、同じ考えは エラーになります。

Dim c As Collection
Set c = New Collection
c.Add "old"

c(1) = "new"            ' コンパイル/実行時エラー — Collection の要素はインデックスでは読み取り専用

Collection.Item読み取り専用として公開しています。c(1)取得 することはできても、それに代入することはできません。要素を「変える」には、 それを取り除いて差し替えを追加します。新しいものがどこに入るかは .AddBefore / After 引数で制御します。

c.Remove 1              ' 位置 1 の古い要素を取り出す
c.Add "new", Before:=1  ' 同じ場所に差し替えを戻す

これを頻繁にやっている自分に気づいたら、それは Collection が道具として間違って いるサインです — おそらく欲しいのは配列(位置での編集)か Dictionary(キーでの 編集)です。Collection が最も輝くのは、要素が入ってきて出ていくものの、 その場で書き換えられはしないときです。

キーのルール:一意な文字列、重複はエラー 457、そして Exists がない

.Add は省略可能なキー — 後で数値の位置の代わりに使える文字列 — を取ります。

Dim prices As Collection
Set prices = New Collection
prices.Add 9.99, "apple"      ' 値が先、次にキー
prices.Add 4.5, "pear"

Debug.Print prices("apple")   ' -> 9.99   (キーで検索)

これは Dictionary のように見えますが、その類似こそが罠です。2 つの厳しい制限が あります。

  1. キーは一意でなければなりません。 既存のキーで 2 つ目の要素を追加すると、 「このキーは既にこのコレクションの要素に関連付けられています (エラー 457)」 が出ます。「追加または更新」はありません — キーの重複は容赦のないエラーです。
  2. .Exists がありません。 Collection には、「このキーはもう存在するか?」 ときれいに尋ねる手段がありません。選択肢はどれも不格好です:検索を On Error Resume Next で包んで Err.Number を調べるか、.Add でエラー 457 を 捕まえるかです。
' Collection が強いてくる「このキーはある?」という不格好な儀式
Dim v As Variant
On Error Resume Next
v = prices("banana")
Dim found As Boolean
found = (Err.Number = 0)
On Error GoTo 0

この欠けている .Exists こそ、キーが問題になるときにいつでも Dictionary を 選ぶべき最も明快な理由です:Scripting.Dictionary.Exists(key) を持ち、 キーで上書きでき、重複でも例外を投げません。Collection がまねるだけのキー優先の 構造については、VBA Dictionary を参照してください。

Collection を縮めるルール:逆順に削除する

位置がずれるので、前向きのループの中でインデックスで要素を取り除くと要素を飛ばし ます — 行を削除したり、変化するコレクションに For Each を使ったりするときと 同じ失敗です。要素 2 を取り除くと、古い要素 3 が位置 2 に滑り込みますが、ループは すでにインデックス 3 へ進んでしまっています。

' 壊れた例 — 削除するとインデックスがずれる
Dim i As Long
For i = 1 To c.Count
    If c(i) = "drop" Then c.Remove i   ' 繰り上がった要素を飛ばす
Next i

' 正しい例 — 逆順に数えれば削除しても残りが乱れない
For i = c.Count To 1 Step -1
    If c(i) = "drop" Then c.Remove i
Next i

これは VBA For Loop と同じ「逆順反復」のルールであり、 VBA For Each の走査の中で安全に削除できない理由でも あります。ループの途中で所属が縮むときはいつでも、降順に数えましょう。

Collection・Array・Dictionary の使い分け

3 つとも、ものの集まりを保持します。得意な仕事はそれぞれ違います。

  • Array — 位置や数値の作業をする固定サイズの番号付き集合(計算、行列的な アクセス、Range を丸ごと一度に読む)。大量の数値処理には最速で、件数が不明な ときは扱いにくい。VBA Array を参照。
  • Collection — 追加で組み立て、順に読む長さ不明の順序付きリスト。「一致する すべての要素」をため込んで一度ループする、というときに最適。頼りにできるキー検索 も、その場での編集もありません。
  • Dictionary — 高速な検索、重複削除、「これ前に見たか?」が必要なときの キー → 値ストア。その .Exists とキーでの上書きは、まさに Collection に 欠けているものです。VBA Dictionary を参照。

手早い見分け方:順に要素を追加する → Collection。名前で引く、あるいは重複を 削除する → Dictionary。固定サイズの数値をゴリゴリ計算する → Array

ExcelMaster の活用

正しいコンテナを選ぶこと — そして Collection が 1 始まりで、上書きできず、 Exists を持たないことを覚えておくこと — は、本当に欲しかった結果に近づく前に 時間を吸い取る、低レベルの判断の類です。

ExcelMaster なら、 それを飛ばせます。結果を説明するだけ — 「C 列で 2 回以上現れる顧客をすべて列挙して」 あるいは「延滞している請求書をすべて集計シートに集めて」 — すれば、そのロジックを 書いて実行し、正しい構造(重複削除にはセット、ため込みには順序付きリスト)を選んで くれます。CollectionDictionary を手で天秤にかける必要はありません。定期 実行するマクロを保守しているなら、これからもコンテナは自分で選ぶことになるでしょう。 しかし日々の単発の作業なら、目的を述べる方が、データ構造を手で正しく決めるより 速いのです。

よくある質問

VBA の Collection に要素を追加するには?

Set c = New Collection で作り、c.Add value を呼びます。リストは自動で伸びます — 宣言するサイズはありません。後で検索するために任意でキーを渡せ (c.Add value, "mykey")、挿入位置は Before / After で制御できます。要素は c(1)(1 始まり)か c("mykey") で読み戻し、c.Count で数えます。

VBA の Collection は 0 始まりですか、1 始まりですか?

1 始まりです。最初の要素は c(1)、最後は c(c.Count) で、c(0) は実行時 エラー 5 を投げます。これは既定で 0 始まりの VBA 配列とは正反対で — 1 つのマクロで 両方を使うと 1 つずれるバグの定番の原因になります。

VBA の Collection と Dictionary の違いは何ですか?

Collection は追加と順序どおりの読み取りに最適化された順序付きリスト、 Scripting.Dictionary は検索に最適化されたキー → 値ストアです。決定的な違いは: Dictionary.Exists(key) を持ち、キーで値を上書きできますが、Collection は どちらも持ちません — 重複キーの追加はエラー 457 を投げ、要素はインデックスでは 読み取り専用です。キーや重複削除が問題になるときはいつでも Dictionary を使い ましょう。

Collection への追加でエラー 457 が出るのはなぜですか?

エラー 457 —「このキーは既にこのコレクションの要素に関連付けられています」— は、 すでに存在するキーで .Add value, key を呼んだことを意味します。Collection の キーは一意でなければならず、事前に確認する組み込みの手段はありません。キーを別に 記録するか、エラーを捕まえるか、あるいは .Exists チェックで問題を丸ごと避けられる Scripting.Dictionary を使いましょう。

Collection にキーが存在するかを確認するには?

きれいな方法はありません — それが Collection の最大の弱点です。検索を On Error Resume Next で包み、c(key) を試み、Err.Number を調べる(0 なら 存在する)しかありません。このチェックが定期的に必要なら、Scripting.Dictionary に切り替えて d.Exists(key) を使いましょう。

検証環境

検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-01。

関連ガイド: VBA Dictionary · VBA Array · VBA For Each · VBA For Loop · VBA With