List と配列 (T[]) の違いを押さえる ── 初学者向けガイド
はじめに
C# を学び始めると int[] nums = { 1, 2, 3 }; のような 配列 と
var list = new List<int> { 1, 2, 3 }; のような List<T> が並行して登場します。
どちらも「複数の要素をまとめる」ための型ですが、設計思想・メモリ管理・使い勝手が大きく異なります。本記事では 6つの観点 で両者を比較し、最後に「いつどちらを選ぶべきか」という指針をまとめます。
配列 (T[]) とは
- 固定長 の連続メモリ領域を確保する、最もプリミティブなコレクション
- CLR が直接サポートするため 要素アクセスが最速
- 型は 参照型(object から派生)だが、int[] などの 値型要素 を持つことも可能
- 一度確保したサイズは不変。追加・削除には Array.Resize などで 新しい配列を再生成 する必要がある
int[] scores = new int[3]; // 要素数3で確保
scores[0] = 85;
List<T> とは
- System.Collections.Generic 名前空間で提供される ジェネリック クラス
- 内部では 配列をラップ し、サイズ超過時に自動的に再確保 (容量を約2倍)
- Add, Remove, Insert, Sort, Find など 豊富なメソッド を具備
- キャパシティ自動拡張のおかげで 可変長 コレクションとして直感的に扱える
var scores = new List<int> { 85 };
scores.Add(92); // サイズを意識せず要素を追加
6つの観点で比較
観点 | 配列 (T[]) | List<T> |
---|---|---|
サイズ変更 | 不可 (再生成が必要) | 可能 (内部で容量を再確保) |
メモリ配置 | 要素が連続配置。Overhead 最小 | 内部配列 + List オブジェクト分の Overhead |
要素アクセス速度 | 最速 (境界チェックのみ) | わずかに遅い (List オブジェクト経由) |
メソッド数 | Length など最小限 | 追加・削除・検索・並べ替えなど豊富 |
LINQ/foreach | どちらも対応 | どちらも対応 (差はない) |
用途の典型例 | 固定長データ、低レベル最適化 | 可変長データ、ビジネスロジック全般 |
Tip: List<T>.Capacity は現在確保している内部配列の長さを示します。大量追加が確定している場合は、先に Capacity を設定して再確保を 1 回に抑えるとパフォーマンスが向上します。
パフォーマンスのイメージ
// List<T> は自動で容量を2倍前後に拡張する
var list = new List<int>(2); // Capacity = 2
list.AddRange(new[]{1,2});
list.Add(3); // Capacity 4 に拡張(再確保コスト発生)
// Array.Resize は毎回コピーが走る
int[] arr = {1,2};
Array.Resize(ref arr, 3); // 新配列を作成してコピー
arr[2] = 3;
- 少量の追加なら List のコストは誤差ですが、数万件以上 を段階的に追加する場合は Capacity を最初に見積もるか、ArrayPool<T> を検討すると良いでしょう。
どちらを選ぶ?── 実務での指針
要件 | 推奨 |
---|---|
要素数が コンパイル時に決定/変更しない | 配列 |
動的に追加・削除 したい | List<T> |
最高速を追求する低レベル API | Span<T> / Memory<T> / 配列 |
Unity など GC を避けたい 場面 | 配列 or NativeArray など |
LINQ・検索・並べ替えを多用 | List<T> |
よくある落とし穴
- 再確保コストの無視
var l = new List<int>();
for (int i = 0; i < 1_000_000; i++) l.Add(i); // 階段的に容量倍増 → 何度もコピー
- ➡ new List<int>(1_000_000) で初期容量を指定しましょう。
- ToArray() vs AsSpan() の使い分け
- ToArray() は 完全コピー です。読み取り専用で良いなら AsReadOnly() や Span<T> を検討。
- 多次元データの誤用
- int[,] (多次元配列) は行列計算で便利ですが foreach が遅くなりがち。
- 大規模データなら ジャグ配列 (int[][]) や List<List> の方がキャッシュ効率が良いケースもあります。
まとめ
キー概念 | 配列 | List<T> |
---|---|---|
特徴 | 固定長・最速アクセス | 可変長・メソッド豊富 |
適材適所 | 高速処理・GC 圧縮 | ビジネスロジック・柔軟性 |
- 固定長・パフォーマンス優先 → 配列
- 柔軟性・開発効率優先 → List<T>
現場では2つを併用することがほとんどです。アルゴリズム部分は配列で書き、外からは IReadOnlyList<T>インターフェースで隠蔽しておけば、将来 List でも配列でも差し替え可能になります。
「道具箱にドライバーと電動ドライバーが両方入っている」イメージで、用途に合わせて選択する習慣を身につけましょう。
訪問数 3 回, 今日の訪問数 3回
ディスカッション
コメント一覧
まだ、コメントがありません