PR
アフィリエイト広告を利用しています
アフィリエイト広告を利用しています

【コピペOK】VBAの配列の使い方|Dim・ReDim Preserve・UBoundからセル範囲の一括読み込みまで(2万行が20倍以上速くなった実測つき)

VBAの配列の使い方(Dim・ReDim Preserve・UBound)のアイキャッチ EXCEL VBA講座
スポンサーリンク

【この記事について】この記事のコードは、Windows 11 の Excel(バージョン 16.0/ビルド 20326・64ビット)で2026年9月26日に実際に実行し、表示される結果とエラー番号を確かめたものです。処理時間はパソコンの性能や Excel の設定で変わるため、目安として読んでください。

VBAの配列は、同じ種類のデータを「番号つきの箱の列」にまとめて入れる仕組みです。Dim 名前(2) と書くと 0・1・2 の3つの箱ができ、For と UBound で順番に取り出せます。さらにセル範囲を Range("A2:D6").Value で配列へ一度に読み込めば、セルを1つずつ触るより大幅に速くなります。筆者の環境では2万行の処理が約1.0秒から約0.05秒に縮みました。この記事では、配列の作り方から、よく出るエラー3つの直し方まで、実行して確かめたコードで順に説明します。

VBAの配列でセル範囲を一度に読み込み、配列の中で計算してから一度に書き戻す流れと、2万行での処理時間の比較
スポンサーリンク

配列とは「番号つきの箱」を一列に並べたもの

ふつうの変数は、箱が1つだけです。担当者が4人いれば変数を4つ作ることになり、人数が増えるたびにコードを書き足さなければなりません。

配列なら、1つの名前の下に箱をいくつも並べ、番号(添字)で区別できます。「3番目の人」は members(2) のように書きます。番号で指定できるので、For で「0番から最後まで」を順に処理でき、人数が増えてもコードはそのままで済みます。

最初に覚えておきたいのは次の3点です。

  • 番号はふつう 0 から始まる(Dim fruits(2) は 0・1・2 の3つ)
  • 最初の番号は LBound、最後の番号は UBound で調べられる
  • セル範囲から読み込んだ配列だけは 1 から始まる(後で説明します)

基本1:大きさを決めて作る(Dim と For)

入れる数が決まっているときは、Dim で大きさを指定して作ります。( ) の中に書くのは「個数」ではなく「最後の番号」です。ここが最初のつまずきどころです。

Sub Sample1_StaticArray()
    Dim fruits(2) As String    ' 0~2 の3つ分の箱ができる
    Dim i As Long

    fruits(0) = "りんご"
    fruits(1) = "みかん"
    fruits(2) = "ぶどう"

    For i = 0 To 2
        Debug.Print i & ": " & fruits(i)
    Next i
End Sub

イミディエイトウィンドウ(Ctrl+G で開きます)には、次のように表示されます。

0: りんご
1: みかん
2: ぶどう

Dim fruits(2) で箱は3つです。「3つ欲しいから 3 と書く」と箱は4つになり、最後の1つが空のまま残ります。

基本2:Array 関数でまとめて作る(LBound・UBound)

中身が最初から決まっているなら、Array 関数で1行にまとめられます。このとき、変数は Variant 型で宣言します。

Sub Sample2_ArrayFunction()
    Dim members As Variant
    Dim i As Long

    members = Array("佐藤", "鈴木", "高橋", "田中")

    For i = LBound(members) To UBound(members)
        Debug.Print i & ": " & members(i)
    Next i
    Debug.Print "件数: " & (UBound(members) - LBound(members) + 1)
End Sub
0: 佐藤
1: 鈴木
2: 高橋
3: 田中
件数: 4

ループの範囲を 0 To 3 と数字で書かず、LBound(members) To UBound(members) と書くのがコツです。あとで人数を足しても、ループの範囲を直す必要がありません。件数は UBound − LBound + 1 で求めます。

基本3:あとから広げる(ReDim Preserve)

「条件に合う人だけ集めたい」のように、いくつ入るか実行するまで分からないときは、大きさを決めずに Dim names() As String と宣言し、必要になったら ReDim で広げます。

次のコードは、A列に担当者、B列に件数が入った表(2〜6行目)から、件数が100以上の人だけを集めます。

Sub Sample3_ReDimPreserve()
    Dim names() As String
    Dim cnt As Long
    Dim r As Long

    cnt = 0
    For r = 2 To 6
        If Cells(r, 2).Value >= 100 Then
            ReDim Preserve names(cnt)    ' 1つ広げる(中身は残す)
            names(cnt) = Cells(r, 1).Value
            cnt = cnt + 1
        End If
    Next r

    If cnt = 0 Then
        Debug.Print "該当なし"
    Else
        Debug.Print cnt & "人: " & Join(names, "、")
    End If
End Sub

B列が「120・80・150・99・100」のとき、結果は次のとおりでした。

3人: 佐藤、高橋、伊藤

ポイントは Preserve です。Preserve を付けずに ReDim すると、それまで入れた中身が消えて空に戻ります。また、1人も該当しなかったときは ReDim が一度も実行されず、配列の大きさが決まっていない状態のままです。その状態で Join や UBound を使うとエラーになるため、先に cnt = 0 かどうかを確かめています(全員を100未満にして実行し、「該当なし」と出ることも確認しました)。

本命:セル範囲を配列へ一度に読み込む

実務で配列がいちばん役に立つのは、ここです。変数 = Range("A2:D6").Value の1行で、表がまるごと配列に入ります。

次のコードは、A列に品名、B列に単価、C列に数量が入った5行の表について、D列に金額(単価×数量)を計算して書き戻します。

Sub Sample4_RangeToArray()
    Dim data As Variant
    Dim r As Long

    data = Range("A2:D6").Value    ' 5行×4列が一度に入る

    Debug.Print "行: " & LBound(data, 1) & "~" & UBound(data, 1) & _
                " / 列: " & LBound(data, 2) & "~" & UBound(data, 2)

    For r = 1 To UBound(data, 1)
        data(r, 4) = data(r, 2) * data(r, 3)    ' D列 = 単価 × 数量
    Next r

    Range("A2:D6").Value = data    ' まとめて書き戻す
End Sub
行: 1~5 / 列: 1~4

表示を見ると分かるとおり、セル範囲から作った配列は「行・列」の2次元で、番号は 1 から始まります。data(1, 1) が範囲の左上(ここでは A2)です。Dim や Array で作った配列は 0 始まりなので、ここだけ番号の数え方が違います。

計算は配列の中で済ませ、最後に Range("A2:D6").Value = data で一度に書き戻します。実行すると、D列には 1200・900・1000・1080・600 が入りました。

どれくらい速くなるのか(2万行で実測)

A列に1〜20000の数字を入れ、B列にその2倍を書く処理を、セルを1つずつ扱う書き方と配列の書き方で比べました。

Sub Speed_Cell()
    Dim r As Long
    For r = 1 To 20000
        Cells(r, 2).Value = Cells(r, 1).Value * 2
    Next r
End Sub

Sub Speed_Array()
    Dim data As Variant
    Dim r As Long
    data = Range("A1:B20000").Value
    For r = 1 To 20000
        data(r, 2) = data(r, 1) * 2
    Next r
    Range("A1:B20000").Value = data
End Sub

Sub MeasureSpeed()
    Dim t As Double

    t = Timer
    Speed_Cell
    Debug.Print "セル1つずつ: " & Format(Timer - t, "0.00") & " 秒"

    t = Timer
    Speed_Array
    Debug.Print "配列でまとめて: " & Format(Timer - t, "0.00") & " 秒"
End Sub

3回実行した結果は次のとおりです(Excel を非表示で自動実行、画面更新オン、計算方法は「自動」)。

回 セル1つずつ 配列でまとめて
1回目 1.04 秒 0.05 秒
2回目 0.99 秒 0.04 秒
3回目 1.08 秒 0.04 秒

どちらの書き方でも、B列の結果(1行目が2、20000行目が40000)が同じになることを確かめています。20倍以上の差が出たのは、セルの読み書きのたびに Excel 本体とのやり取りが発生するためです。配列の書き方なら、そのやり取りは「読み込み1回・書き戻し1回」で済みます。処理が遅いマクロは、まずループの中で Cells を触っていないかを見直してみてください。

文字列を分ける・つなぐ(Split と Join)

カンマ区切りの文字を配列に分けるのが Split、配列を1つの文字列につなぐのが Join です。

Sub Sample5_SplitJoin()
    Dim parts As Variant

    parts = Split("東京,大阪,名古屋", ",")

    Debug.Print (UBound(parts) + 1) & "個: " & parts(0) & " / " & parts(2)
    Debug.Print Join(parts, "→")
End Sub
3個: 東京 / 名古屋
東京→大阪→名古屋

Split で作った配列も 0 始まりです。3つに分けたときの最後は parts(2) で、parts(3) ではありません。

よく出るエラー3つと直し方

配列でつまずく原因は、ほとんどが次の3つです。どれも実際に実行して、表示されるエラー番号を確かめました。

エラー9「インデックスが有効範囲にありません」

いちばん多いエラーです。存在しない番号の箱を指定したときに出ます。

Sub Error1_OutOfRange()
    Dim scores(2) As Long    ' 使えるのは 0, 1, 2 だけ
    scores(3) = 80           ' ← ここで実行時エラー9
End Sub

Dim scores(2) で作った箱は 0〜2 なので、scores(3) はありません。大きさを決めていない配列に UBound を使った場合も、同じエラー9になります。

Sub Error2_NotAllocated()
    Dim names() As String    ' まだ大きさが決まっていない
    Debug.Print UBound(names)    ' ← ここで実行時エラー9
End Sub

直し方は、ループの範囲を LBound〜UBound にすることと、ReDim する前に配列を使わないことです。「基本3」のコードで件数を数えていたのは、この2つ目のエラーを避けるためです。エラー9は配列以外(シート名の間違いなど)でも出ます。原因の切り分けは、次の記事で詳しく説明しています。

【もう止まらない】VBA「実行時エラー9」の原因7つと直し方|インデックスが有効範囲にありません
VBAの「実行時エラー9:インデックスが有効範囲にありません」を、シート名・ブック名・配列・Split・コレクションの7原因に分けて解説。コピペで使える存在チェック関数つきで、今日中に直せます。

ReDim Preserve で広げられるのは「最後の次元」だけ

行と列を持つ2次元の配列を Preserve 付きで広げるとき、広げられるのは最後の次元(列のほう)だけです。1次元目を広げようとすると、エラー9で止まります。

Sub Error3_PreserveFirstDim()
    Dim table() As Variant
    ReDim table(1 To 2, 1 To 3)
    ReDim Preserve table(1 To 3, 1 To 3)    ' ← 1次元目は広げられない(エラー9)
End Sub

行を増やしたい表は、最初から十分な大きさで ReDim しておくか、「実務例」のように件数を数え終わってから一度だけ ReDim するのが確実です。

エラー13「型が一致しません」(1セルだけ読み込んだとき)

セル範囲を配列へ読み込むコードは、範囲が1セルだけだと配列になりません。中身はただの値になり、UBound を使うとエラー13で止まります。データが1件しかない日だけ止まるマクロは、たいていこれが原因です。

Sub Error4_SingleCell()
    Dim data As Variant
    data = Range("A2:A2").Value    ' 1セルだけだと配列にならない
    Debug.Print UBound(data, 1)    ' ← ここで実行時エラー13
End Sub

IsArray で配列かどうかを確かめ、配列でなければ1行1列の配列に包み直すと、後ろの処理を変えずに済みます。

Sub Fix4_SingleCell()
    Dim data As Variant

    data = Range("A2:A2").Value
    If Not IsArray(data) Then
        Dim tmp(1 To 1, 1 To 1) As Variant
        tmp(1, 1) = data
        data = tmp    ' 1行1列の配列に包み直す
    End If
    Debug.Print "行数: " & UBound(data, 1) & " / 値: " & data(1, 1)
End Sub
行数: 1 / 値: 佐藤

実務例:担当者ごとの売上合計を一度に出す

最後に、ここまでの内容を組み合わせた例です。A列に担当者、B列に金額が並んだ明細から、担当者ごとの合計をD・E列に書き出します。明細の最終行は自動で調べます。

Sub Sample7_TotalByPerson()
    Dim data As Variant
    Dim result() As Variant
    Dim dic As Object
    Dim lastRow As Long, r As Long, i As Long
    Dim k As Variant

    lastRow = Cells(Rows.Count, 1).End(xlUp).Row
    data = Range("A2:B" & lastRow).Value    ' 明細を配列へ

    Set dic = CreateObject("Scripting.Dictionary")
    For r = 1 To UBound(data, 1)
        dic(data(r, 1)) = dic(data(r, 1)) + data(r, 2)    ' 担当者ごとに足す
    Next r

    ReDim result(1 To dic.Count, 1 To 2)    ' 担当者の人数ぶんの表
    i = 0
    For Each k In dic.Keys
        i = i + 1
        result(i, 1) = k
        result(i, 2) = dic(k)
    Next k

    Range("D1:E1").Value = Array("担当者", "合計")
    Range("D2").Resize(dic.Count, 2).Value = result    ' 一度に書き出す
End Sub

明細が「佐藤 12000・鈴木 8000・佐藤 5000・高橋 15000・鈴木 3000・佐藤 7000・高橋 2000」の7行のとき、D・E列には次のように出ました。

担当者 合計
佐藤 24000
鈴木 11000
高橋 17000

明細の読み込みも、結果の書き出しも1回ずつです。担当者ごとの足し算には Dictionary(名前をキーにして値をためる入れ物)を使い、人数が確定してから ReDim result(1 To dic.Count, 1 To 2) で表を1回だけ作っています。最終行の取り方は、次の記事で詳しく説明しています。

【もう迷わない】VBAで最終行を取得する方法5つと使い分け|End(xlUp)が1を返す・途中の空白・UsedRangeのズレ
VBAで最終行を取るなら、まずは ws.Cells(ws.Rows.Count, 1).End(xlUp).Row の1行です。データ0件でも1が返る・シート指定の漏れ・基準列の空白・Integerのオーバーフローという4つのつまずきと、Find・UsedRange・CurrentRegionとの使い分けを、Microsoft公式ドキュメントをもとにまとめました。

よくある質問

Q. 配列の番号を 1 から始めることはできますか?

A. できます。Dim fruits(1 To 3) As String のように 下限 To 上限 で書けば、1〜3 の3つになります。モジュールの先頭に Option Base 1 と書く方法もありますが、Split の結果など、この設定と関係なく 0 から始まる配列もあります。1 To で明示し、ループは LBound〜UBound で回すほうが混乱しません。

Q. 配列の変数は何型で宣言すればいいですか?

A. 自分で作って中身の型が決まっているなら String や Long、Array 関数の結果やセル範囲を受け取るなら Variant にします。セル範囲の配列には文字と数値が混ざるため、Variant でないと受け取れません。

Q. 配列の中身をまとめて消すには?

A. Erase 配列名 を使います。大きさを決めて作った配列は中身が空に戻り、ReDim で広げた配列は大きさごと消えます。

Q. 配列とコレクション・Dictionary はどう使い分けますか?

A. 件数が決まっている・番号で順に処理するなら配列、名前で探したい・重複をまとめたいなら Dictionary が向いています。実務例のように、読み込みと書き出しは配列、途中の集計は Dictionaryという組み合わせがよく使われます。

まとめ:配列は「読み込み1回・書き戻し1回」で使う

  • Dim 名前(2) は 0〜2 の3つ。( ) の中は最後の番号
  • ループは LBound〜UBound で回せば、件数が変わっても直さなくてよい
  • あとから広げるときは ReDim Preserve。広げられるのは最後の次元だけ
  • セル範囲から作った配列は 2次元・1 始まり。1セルだけだと配列にならない
  • セルを1つずつ触るのをやめて配列でまとめると、2万行で 約1.0秒 → 約0.05秒になった

配列は、最初は番号の数え方で必ず一度つまずきます。でも、エラー9が出たら「箱の番号を数え直す」だけで直ります。まずは「基本1」のコードを貼り付けて実行し、イミディエイトウィンドウに3行出るところから始めてみてください。重かったマクロが一瞬で終わる感覚は、きっとあなたの自信になります。

次に読む

【型が合わない】VBA「実行時エラー13」の原因7つと直し方|型が一致しませんの正体はセルの中身
VBAの「実行時エラー13 型が一致しません」は、コードではなくセルの中身が原因で出ます。文字混じり・#N/Aエラー値・Application.Matchの戻り値・配列・オブジェクトなど原因7つを、[デバッグ]で止まった行から30秒で切り分ける手順で解説。IsNumeric/IsError/IsDateを使った再発しない書き方まで。
【動かない人へ】エクセルのマクロが実行できない原因7つと直し方|有効化・ブロック解除・xlsm保存
エクセルのマクロが実行できない原因は、ほとんど3つ。黄色いバーの「コンテンツの有効化」、赤いバー「セキュリティ リスク」のブロック解除、.xlsmでの保存です。症状別の早見表から、開発タブ・ボタン無反応・トラストセンター設定まで、7つの原因を手順つきで直します。

出典(2026年9月26日 確認)

配列の使用 (VBA)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/using-arrays

ReDim ステートメント (VBA)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/redim-statement

UBound 関数 (Visual Basic for Applications)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/ubound-function

Array 関数 (Visual Basic for Applications)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/array-function

Split 関数 (Visual Basic for Applications)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/split-function

Option Base ステートメント (VBA)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/option-base-statement

下付き文字が有効範囲にありません (エラー 9)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/subscript-out-of-range-error-9

Range.Value プロパティ (Excel)|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/api/excel.range.value

Dictionary オブジェクト|Microsoft Learn
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/dictionary-object


当ブログは、お風呂好きのみなさんとに快適なお風呂生活を楽しんで頂きたい思いで運営しています。
「記事が役に立った」「続きも読んでみたい」と感じていただけたら、下記リンクからのご利用・ご支援で応援してもらえると嬉しいです。

※Amazonのアソシエイトとして、「メリ爺のウチ風呂万歳」は適格販売により収入を得ています。

EXCEL VBA講座
スポンサーリンク
シェアする
メリ爺をフォローする

コメント

タイトルとURLをコピーしました