- 結論
- コレクションに属する個々の要素を列挙する場合には「For~Next ステートメント」の方が「For Each~Next ステートメント」よりも高速である。
Accessには同じ種類のオブジェクトの集まりを表す「コレクション」という概念があります。例えば、データベースは複数のテーブルや複数のフォーム、複数のレポートなどから構成されています。1つのテーブルは複数のフィールドの集まりです。1つのフィールドはさまざまなプロパティ(属性)が集まることによってその特性が決められています。またフォームも複数のコントロールが集まって構成されていますし、1つ1つのコントロールもまたさまざまなプロパティの集合体です。
コレクションに属する個々の要素を順番に抜き出して一連の処理を行いたい場合に便利なのが「For Each~Next ステートメント」です。このステートメントの使用にあたっては、コレクション内の要素数を知ることなくすべての要素を列挙することができます。また、個々の要素はあらかじめ宣言されたオブジェクト変数(fld As Field...など)に自動的に代入されます。
ところで、このコレクションもまた1つのオブジェクトであり、いくつかのプロパティを持っています。その中の[Count]プロパティを調べることによってコレクションに含まれる要素数を取得することができます。また、コレクションの各要素はゼロから始まるそれぞれ一意のインデックスを持っています。したがって、[Count]プロパティをループの上限値として、またループカウンタ変数を要素のインデックス値として扱うことによって、「For~Next ステートメント」によってもコレクション要素の列挙を行うことができます。
そこでここでは、あるテーブルを構成する"フィールドの集まり"である[Fields]コレクションについて、すべての要素名つまり"フィールド名"を列挙するプログラムを For Each~Next を使った場合と For~Next を使った場合とで、その処理時間を測定・比較してみたいと思います。
テストコードは次のようなものです。
Dim dbs As Database
Dim tdf As TableDef
Dim fld As Field
Dim strFldName As String
Dim intFldLoop As Integer
Dim iintLoop As Integer
Const clngTestCnt As Long = 1000 'テスト回数のループカウンタ
Set dbs = CurrentDb
Set tdf = dbs.TableDefs("tblFieldTest")
ts_Watch "テスト開始", True
For iintLoop = 1 To clngTestCnt
For Each fld In tdf.Fields
strFldName = fld.Name
Next fld
Next iintLoop
ts_Watch "For Each~Next"
For iintLoop = 1 To clngTestCnt
For intFldLoop = 0 To tdf.Fields.Count - 1
Set fld = tdf.Fields(intFldLoop) '添え字は0から始まる
strFldName = fld.Name
Next intFldLoop
Next iintLoop
ts_Watch "For~Next"
そして、テスト結果はつぎのようなものになりました。
Do~Loop |
2.54 Sec |
For~Next |
1.72 Sec |
コレクションの要素を扱う方法の例ではしばしば「For Each~Next ステートメント」が出てきて、あたかもそれがお決まりの手法のように思っていましたのでこれは意外な結果でした。明らかに"コレクションに属する個々の要素を列挙する場合には「For~Next ステートメント」の方が「For Each~Next ステートメント」よりも高速である。"という結果になっています。
上記のテスト結果はあくまでも1000回ものテストの累積時間差です。実際には、あらゆるVBAのコードで For~Next が使われる頻度に比べてコレクションを扱う頻度は極端に少ないでしょうし、コレクションを扱う場合にも要素数もそう多くないと思われます。そう考えると上記の結果は「コレクションでは For~Next を使うべし」と言うほどではないでしょう。簡単なコードで済ませたい場合には For Each~Next、ループの中でループカウンタ変数を要素の添え字以外にも使いたいような場合には For~Next、と状況に応じて使い分ければいいと思います。 |