TL;DR — VBA の 2D 配列は、二つ の添字を持つ 一つ の配列 — 行と列のグリッド、
g(row, column)— であって、配列の配列ではありません。Dim g(1 To 3, 1 To 4)は 3 行 × 4 列です。 Excel の作業で最も役立つ一つの事実はこれです。Range("A1:D3").Valueを読むと 1 始まりの二次元 Variant 配列 が一手で手に入り、Range(...).Value = arrで配列を書き戻すと一手で書き込まれます — だからこそ配列処理は、セルを一つずつループするよりも劇的に速いのです。
Sub SheetToArrayAndBack()
Dim data As Variant
data = Range("A1:D100").Value ' シートに一度アクセス -> 1 始まりの 2D 配列
Dim r As Long
For r = 1 To UBound(data, 1) ' 行
data(r, 4) = data(r, 2) * data(r, 3) ' D 列 = B 列 * C 列、メモリ上で
Next r
Range("A1:D100").Value = data ' 一度で書き戻す -> 瞬時
End Sub
2D 配列は、配列とワークシートが出会う場所です。シートは まさに グリッドであり、セルのブロックを扱う最速
の方法は、それを 2D 配列に取り込み、メモリ上で計算し、書き戻すことです。これは
ReDim で配列にサイズを与える ことと UBound でその形を読む
ことの上に、そのまま積み上がっています — グリッドは次元ごとに上限・下限を持つだけです。人がつまずく唯一の
点は、Range.Value の配列が 1 始まり であることと、ReDim Preserve では行を足せないことです。
この記事で学べること
- 考え方の軸 — 二つの添字を持つグリッド
g(row, column)であって、入れ子の配列ではない - 2D 配列を
Dim g(1 To 3, 1 To 4)とg(r, c)で宣言し、指定する - いちばん大事なこと —
Range.Valueは 1 始まり の 2DVariant配列を一手で与える - 配列を範囲へ一度で書き戻す — そしてそれがなぜこれほど速いのか
- 二つの境界ケース — 単一セルは配列では ない、単一列は
(n, 1)になる - 行を足す — なぜ
ReDim Preserveにはできないのか、そして代わりにどうするか - ジャグ配列(配列の配列)が本当の 2D 配列とどう違うか
考え方の軸:一つの配列、二つの添字
一次元配列は、番号の付いた箱の一列です。a(i)。二次元配列は グリッド です — 各箱を二つの数、行と列で
指定します。g(r, c)。肝心なのは、これは二通りに指定されるだけの 一つの 配列であって、要素それ自体がリ
ストであるようなリストではない、ということです。
Dim g(1 To 3, 1 To 4) As Long ' 3 行、4 列 = 12 要素
g(1, 1) = 10 ' 1 行目、1 列目
g(3, 4) = 99 ' 3 行目、4 列目
最初の添字は慣例的に行、二つめが列です — セル参照を読む順番と同じです。この順番を一貫させれば、2D 配列は セルの長方形のブロックへきれいに対応します。
グリッドを宣言してループする
二つの上限・下限を宣言し、それから入れ子の For でループします — 外側で行、内側で列 —
次元番号付きの UBound を使って。
Dim g(1 To 3, 1 To 4) As Long
Dim r As Long, c As Long
For r = LBound(g, 1) To UBound(g, 1) ' 行
For c = LBound(g, 2) To UBound(g, 2) ' 列
g(r, c) = r * 10 + c
Next c
Next r
UBound(g, 1) は行数、UBound(g, 2) は列数です。各次元で LBound から UBound までループすれば、配列が
1 To n(Range.Value が与える形)でも 0 始まりでも、同じコードが動きます。
いちばん大事なこと:Range.Value は 1 始まりの 2D 配列
これこそ、2D 配列を学ぶ価値を生む事実です。複数セルの範囲の .Value を読むと、一手で、上限・下限が 1
から始まる二次元 Variant 配列 が手に入ります — 範囲がシートのどこにあってもです。
Dim data As Variant
data = Range("C5:E7").Value ' シート上のどこかにある 3 行 3 列のブロック
' data はいま data(1 To 3, 1 To 3) - つねに 1 始まり、行が先で列が後
Debug.Print data(1, 1) ' C5 の値
Debug.Print data(3, 3) ' E7 の値
二つのことに注意してください。第一に、配列は 0 始まりではなく 1 始まり です — なので data(1, 1) が左
上のセルであり、For i = 0 のループは最初の行を取りこぼして実行時エラー 9 にぶつかります。第二に、添字は
ブロックに対する相対 であって、シートに対してではありません。data(1, 1) は、範囲が A1 でも C5
でも、その範囲の左上のセルです。受け取る変数は Variant として宣言してください(As Long() などではな
く) — VBA が新しい 2D Variant を埋めてくれます。
一度で書き戻す
同じ技は逆向きにも効きます。正しい形の 2D 配列を範囲の .Value に代入すれば、Excel はブロック全体を一度
に書き込みます。
Range("C5:E7").Value = data ' 9 セルすべてを一回の操作で書き込む
これが、配列のやり方が速い理由です。セルを一つずつ触ると、読み書きのたびに VBA と Excel の境界をまたぎま
す — 大きな範囲なら何千回もの往復です。配列に読み込み、メモリ上で計算し、書き戻すなら、境界をまたぐのは合
計 二回 だけです。数百セルを超えるものなら、これは瞬時に感じられるマクロと、じりじり動くのを眺めるマ
クロとの違いになります。書き込む範囲は配列の形(行 × 列)と一致していなければならないので、対象の範囲を
UBound(arr, 1) 行 × UBound(arr, 2) 列にサイズ指定しましょう。
かみついてくる二つの境界ケース
単一セルは配列ではありません。 Range("A1").Value は 1 To 1, 1 To 1 の配列ではなく、素のスカラー値
を返します — なので配列を前提としたコードは、一セルの範囲で失敗します。範囲が単一セルの可能性があるなら、
添字を付ける前に IsArray() で判定しましょう。
単一の行や列でも、やはり 2D です。 Range("A1:A10").Value は data(1 To 10, 1 To 1) — 10 行 1 列 —
なので、data(i) ではなく data(i, 1) として指定します。単一の行でも同じで、data(1, j) です。シートの
「一次元」の切れ端では、二つめの添字を忘れがちです。
行を足す:ReDim Preserve にはできない
ReDim Preserve は最後の次元しかリサイズできない ので、データを保ったまま 2D 配列
の行数(最初の次元)を増やすことは できません。実用的な選択肢は三つです。
- 一度でサイズを決める。 行数が事前に分かっているなら(最終行、レコード数)、ループの前にグリッドを最
終的なサイズへ
ReDimし、Preserveはいっさい使いません。 - 代わりに列を伸ばす。 拡張する次元が最後になるよう配列を列 × 行として宣言し、出力時に
Application.Transposeで転置します。 - 作り直す。 本当に増え方が分からないなら、
Collectionやジャグ構造に集めておき、最後に一度だけ 2D 配列を組み立てます。
シートの作業では、最初の選択肢がほぼいつも当てはまります。行数は、始める前に安上がりに知れるものだからで す。
ジャグ配列は別物
本当の 2D 配列は長方形です — どの行も同じ列数を持ちます。ジャグ 配列は、要素が それ自体 配列である ような一次元配列なので、各「行」が異なる長さを持てます。二つの添字ではなく、二組の角かっこで指定します。
Dim jagged(1 To 2) As Variant
jagged(1) = Array(1, 2, 3) ' 3 要素の行
jagged(2) = Array(9, 8) ' 2 要素の行
Debug.Print jagged(1)(3) ' 3 <- (1, 3) ではなく、二組の角かっこ
ジャグ配列は不ぞろいのデータに便利ですが、つねに長方形である Range.Value とは 相互運用できません。
データが長方形なら — シートのブロックはつねにそうです — 本当の 2D 配列を使いましょう。
ExcelMaster の活用
セルのブロックを効率よく扱うのは一つのパターンです — Range.Value を 1 始まりの 2D 配列に読み込み、メモ
リ上で計算し、一度で書き戻す — そして罠はどれも細部にあります。1 始まりの添字、単一セルのスカラー値、
Preserve では増やせない行です。
ExcelMaster には、やりたいことをその
まま伝えられます — 「使用範囲の各行について、数量かける単価を E 列に入れて」 — すると、ブロックを 2D 配列
に読み込み、1 から UBound(arr, 1) までループし、結果を一回の代入で書き戻し、単一セルや単一列のケース
も処理するので、マクロは正しく、かつ速くなります。ブックとコードはあなたの手元に残ります。
よくある質問
VBA で 2D 配列を宣言するには?
上限・下限を二つ、カンマで区切って与えます。Dim g(1 To 3, 1 To 4) As Long は 3 行 4 列のグリッドを作り
ます。要素は二つの添字 g(row, column) で指定します。たとえば g(2, 3) です。動的な 2D 配列なら、
Dim g() で開いたまま宣言し、あとで ReDim g(1 To 3, 1 To 4) でサイズを与えます。
VBA で範囲を 2D 配列に読み込むには?
範囲の .Value を Variant に代入します。Dim data As Variant: data = Range("A1:D100").Value。VBA は一
手で 1 始まり の二次元配列 data(1 To rows, 1 To columns) を埋めます。セルは data(row, column) で指
定し、添字は範囲の左上に対する相対です。これが多数のセルを処理する最速の方法です。
Range.Value の配列が 0 始まりではなく 1 始まりなのはなぜですか?
Excel がそう返すからです — Range.Value の配列は、範囲がシートのどこにあっても、両方の次元で必ず添字 1
から始まります。LBound(data, 1) から UBound(data, 1) まで(あるいは単に 1 To UBound)ループすれば、
0 始まりのループで最初の行を飛ばしたり、末尾を越えて走ったりせずに済みます。
2D 配列を範囲へ書き戻すには?
範囲の .Value に代入します。Range("A1:D100").Value = data。範囲は配列と同じ形 — 同じ行数と列数 — でな
ければならないので、対象を UBound(data, 1) 行、UBound(data, 2) 列にサイズ指定します。これはブロック全
体を一回の操作で書き込み、セルを一つずつよりもはるかに速いです。
VBA で 2D 配列に行を追加できますか?
ReDim Preserve ではできません — 最後の(列の)次元しかリサイズできないので、行数を増やすと実行時エラー
9 になります。ループの前に配列を最終的な行数へサイズ指定するか、列 × 行として宣言して転置するか、
Collection から組み立てて最後に一度だけ 2D 配列を作りましょう。
検証環境
動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-09-24.
関連ガイド: VBA ReDim · VBA UBound · VBA Array · VBA For Loop · VBA Range
