【実務・中級編】VB.NETの配列とList(Of T)の性能比較:実務で選ぶべきコレクションとメモリ効率化の鉄則 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

配列か、List(Of T)か。VB.NETのメモリ管理を極める「生存戦略」

現場でコードを見ていると、脊髄反射で「とりあえず配列」あるいは「とりあえずList」を使っているケースが後を絶たない。VB.NETという言語は、親しみやすさゆえに「裏で何が起きているか」を隠蔽しがちだ。しかし、システムが数万件、数十万件のレコードを扱うようになった瞬間、その「隠蔽」が致命的なボトルネックとなって牙を剥く。

今回は、伝説的アーキテクトの視点から、配列と`List(Of T)`の境界線、そしてメモリ効率を最大化する設計思想を伝授する。

—

1. 根本的な違い:静的な「箱」と動的な「管理機構」

配列 (Array)

メモリ上に連続した固定領域を確保する。インデックスアクセスは最強に速い。しかし、一度確保したサイズは変更できない。サイズを変えるには、新しい配列を確保し、全要素をコピーする(`ReDim Preserve`)必要がある。これは巨大なデータセットでは自殺行為に近いコストだ。

List(Of T)

内部的には配列を保持しているが、「容量(Capacity)」と「要素数(Count)」を分離管理している。要素を追加する際、容量が足りなくなれば、内部で2倍のサイズの配列を再確保し、旧配列をコピーする仕組みだ。

—

2. どちらを選ぶべきかの「極限の判断基準」

実務においての選択基準は、以下の3点に集約される。

1. 要素数が確定しているか?

  • Yes:迷わず配列。メモリ効率・速度ともに最強。
  • No:List(Of T)。

2. 頻繁な検索が必要か?

  • インデックス指定のアクセスが主なら配列。複雑な抽出条件が必要ならLINQと相性の良いList。

3. データ寿命は?

  • メソッド内の一時的な計算なら配列。クラスのプロパティとして保持し、追加・削除が繰り返されるならList。

—

3. 実装の鉄則:メモリ断片化を防ぐ「Capacityの事前確保」

`List(Of T)`を使う際、最も犯しやすいミスが「容量の初期化を怠ること」だ。デフォルトのListは、要素が追加されるたびに内部で何度も配列の再確保(リサイズ)を行い、メモリを浪費する。

もし、処理対象の件数が予測できるなら、必ず`Capacity`を指定せよ。

.net
”’

”’ 大規模データ処理における最適化されたリスト生成の例
”’

Public Sub ProcessLargeData(sourceData As IEnumerable(Of String))
‘ 件数が予測できる場合、Capacityを先に指定する
‘ これにより、実行時の内部配列再確保(コピー処理)をゼロに抑える
Dim count As Integer = sourceData.Count()
Dim resultList As New List(Of String)(count)

For Each item In sourceData
‘ このAdd処理は非常に高速。再確保が発生しないため。
resultList.Add(item)
Next
End Sub

—

4. プロダクションコード:保守性と堅牢性を両立する設計

データベースやファイルから読み込む際、Listで加工し、最終的に固定データとして扱うなら、最後に`ToArray()`で変換するのが鉄則だ。これにより、外部への不用意な変更を遮断できる。

.net
Public Function GetProcessedData(rawFilePath As String) As String()
‘ 読み込み行数が不明な場合はListで受ける
Dim buffer As New List(Of String)()

Using reader As New System.IO.StreamReader(rawFilePath)
While Not reader.EndOfStream
Dim line As String = reader.ReadLine()
‘ バリデーション:堅牢なシステムは入力で弾く
If Not String.IsNullOrWhiteSpace(line) Then
buffer.Add(line.Trim())
End If
End While
End Using

‘ 呼び出し元には「変更不可」な配列として返すのが作法
Return buffer.ToArray()
End Function

—

5. アーキテクトからの助言:なぜこの書き方が「正義」なのか

  • なぜ `List.ToArray()` を使うのか?

Listをそのまま戻り値にすると、呼び出し元で`.Add()`や`.Clear()`ができてしまう。これは「データの完全性」を破壊する温床だ。戻り値は配列(あるいは`IEnumerable`)に固定し、内部の可変性をカプセル化する。これがバグを未然に防ぐ防御的プログラミングだ。

  • メモリ効率の観点

もし要素が100万件あるなら、`List(Of T)`をそのまま保持し続けることは、配列の約2倍のメモリを食いつぶす可能性がある(Capacityが要素数よりも多めに確保されるため)。用途が終われば`Clear()`を呼び出し、参照を`Nothing`に倒すことでGC(ガベージコレクション)を促す気配りが必要だ。

まとめ

  • 固定長なら配列。 迷う必要はない。
  • 可変長ならList。 ただし、件数が予測できるなら必ずコンストラクタで`Capacity`を確保せよ。
  • クラスの境界線をまたぐときは配列化。 内部の可変性を外部に漏らさない。

プロのコードとは、単に動くものではない。「メモリの挙動を支配し、未来のバグを設計段階で排除したもの」を指す。今日から、君のコードのパフォーマンスは劇的に変わるはずだ。

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