C#のListをClearで空にする方法|使い方・注意点・メモリ解放との違いを解説
はじめに
C#でList<T>を使っていると、「中身をすべて削除したい」「一度使ったListを空にして再利用したい」という場面がよくあります。そのときに使う代表的なメソッドがClearです。
List.Clear()を呼び出すと、Listに入っている要素をすべて削除できます。たとえば、文字列の一覧、数値の一覧、検索結果、一時的なバッファなどをまとめて空にしたい場合に便利です。
ただし、Clearは「Listの中身を空にする」ためのメソッドであり、「Listそのものを削除する」「必ずメモリを即座に解放する」という意味ではありません。null代入やnew List<T>()で作り直す場合とは挙動が異なります。
この記事では、C#のListをClearで空にする基本的な使い方から、CountやCapacityの変化、メモリ解放との違い、実務での注意点まで解説します。
1. C#のListをClearで空にする基本
1-1. List.Clearとは何か
List.Clearとは、C#のList<T>に格納されているすべての要素を削除するためのメソッドです。Microsoftの公式ドキュメントでも、List<T>.Clearは「List<T>からすべての要素を削除するメソッド」と説明されています。Microsoft Learn
たとえば、次のようなListがあるとします。
C#List<string> names = new List<string>
{
"Alice",
"Bob",
"Charlie"
};
このnamesには3つの要素が入っています。
C#names.Clear();
このようにClearを呼び出すと、namesの中身は空になります。
重要なのは、ClearはList変数そのものを消すわけではないという点です。あくまで、Listの中に入っている要素をすべて削除します。
1-2. Clearの基本構文
Clearの基本構文は次のとおりです。
C#list.Clear();
たとえば、List<int>であれば次のように使います。
C#List<int> numbers = new List<int> { 10, 20, 30 };
numbers.Clear();
Clearには引数がありません。削除する要素を指定する必要はなく、呼び出したListの要素がすべて削除されます。
戻り値もありません。つまり、Clearを実行した結果を別の変数に受け取るような使い方はしません。
C#numbers.Clear(); // OK
次のような書き方はできません。
C#var result = numbers.Clear(); // エラー
Clearは、Listの状態を変更するメソッドです。呼び出した時点で、そのListの中身が空になります。
1-3. Clearを使うとListの要素数はどう変わるか
Clearを使うと、Listの要素数を表すCountは0になります。Countは、現在Listの中に実際に入っている要素数を表すプロパティです。公式ドキュメントでも、CountはList<T>に含まれる要素数を取得するプロパティとして説明されています。Microsoft Learn
次の例を見てみましょう。
C#List<string> fruits = new List<string>
{
"Apple",
"Banana",
"Orange"
};
Console.WriteLine(fruits.Count); // 3
fruits.Clear();
Console.WriteLine(fruits.Count); // 0
Clear前は3件の要素があるため、Countは3です。Clear後はすべての要素が削除されるため、Countは0になります。
このように、Listが空かどうかを確認したい場合は、Countを見るのが基本です。
C#if (fruits.Count == 0)
{
Console.WriteLine("Listは空です。");
}
Clearを実行した後は、Listの中に要素が存在しないため、Count == 0になります。
1-4. Clear後もListは再利用できる
Clearを実行しても、List変数そのものは残っています。そのため、Clear後に再びAddで要素を追加できます。
C#List<string> messages = new List<string>();
messages.Add("Hello");
messages.Add("World");
messages.Clear();
messages.Add("C#");
messages.Add("List");
Console.WriteLine(messages.Count); // 2
この例では、一度Clearで中身を空にしたあと、同じListに対して新しい要素を追加しています。
Clearは「Listを使い終わったから完全に捨てる」というよりも、「同じListインスタンスを空の状態に戻す」イメージで使うと理解しやすいです。
たとえば、ループ内で一時的なListを何度も使う場合、毎回new List<T>()で作り直すのではなく、Clearで中身だけを空にして再利用することがあります。
2. List.Clearの使い方をサンプルコードで解説
2-1. 文字列ListをClearで空にする例
まずは、文字列を格納するList<string>をClearで空にする例です。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> colors = new List<string>
{
"Red",
"Green",
"Blue"
};
Console.WriteLine("Clear前:");
foreach (string color in colors)
{
Console.WriteLine(color);
}
colors.Clear();
Console.WriteLine("Clear後のCount: " + colors.Count);
}
}
実行結果は次のようになります。
Clear前:
Red
Green
Blue
Clear後のCount: 0
Clearを呼び出すことで、colorsに入っていた"Red"、"Green"、"Blue"がすべて削除されます。
Clear後は要素がないため、Countは0になります。
2-2. 数値ListをClearで空にする例
Clearは文字列だけでなく、数値のListにも使えます。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> scores = new List<int> { 80, 90, 75, 100 };
Console.WriteLine("Clear前のCount: " + scores.Count);
scores.Clear();
Console.WriteLine("Clear後のCount: " + scores.Count);
}
}
実行結果は次のとおりです。
Clear前のCount: 4
Clear後のCount: 0
List<int>に4つの数値が入っていましたが、Clear後はすべて削除されています。
ClearはList<T>のメソッドなので、Tがstringでもintでも、自作クラスでも同じように使えます。
C#List<double> prices = new List<double> { 120.5, 300.0, 450.75 };
prices.Clear();
List<bool> flags = new List<bool> { true, false, true };
flags.Clear();
型に関係なく、Listの中身をすべて削除したい場合はClearを使えます。
2-3. Clear前後のCountを確認する
Clearの挙動を確認するときは、Countを表示するとわかりやすいです。
C#List<string> items = new List<string>
{
"A",
"B",
"C"
};
Console.WriteLine($"Clear前: {items.Count}");
items.Clear();
Console.WriteLine($"Clear後: {items.Count}");
実行結果は次のようになります。
Clear前: 3
Clear後: 0
Clear前は要素が3つあるためCountは3です。Clear後は要素がすべて削除されるためCountは0です。
Listが空になったかどうかを判定する場合は、次のように書けます。
C#if (items.Count == 0)
{
Console.WriteLine("Listは空です。");
}
また、LINQを使っている場合はAny()で確認することもあります。
C#if (!items.Any())
{
Console.WriteLine("Listは空です。");
}
ただし、単純にList<T>の要素数を確認するだけなら、Count == 0で十分です。
2-4. Clear後にAddで要素を追加する例
Clear後も、同じListに対してAddで要素を追加できます。
C#List<string> tasks = new List<string>
{
"メール確認",
"資料作成",
"会議準備"
};
tasks.Clear();
tasks.Add("コードレビュー");
tasks.Add("テスト実行");
foreach (string task in tasks)
{
Console.WriteLine(task);
}
実行結果は次のとおりです。
コードレビュー
テスト実行
Clearによって以前の要素は削除されていますが、List自体は有効な状態のままです。そのため、Add、Remove、foreachなど、通常のList操作を引き続き行えます。
この特徴は、Listを一時的な作業領域として使い回す場合に便利です。
C#List<int> buffer = new List<int>();
for (int i = 0; i < 3; i++)
{
buffer.Clear();
buffer.Add(i);
buffer.Add(i * 10);
Console.WriteLine(string.Join(", ", buffer));
}
このように、ループのたびにListを初期状態に戻してから再利用できます。
3. Clearとnull代入・new Listの違い
3-1. ClearはListの中身だけを削除する
Clearは、Listの中身だけを削除します。List変数には、同じListインスタンスへの参照が残っています。
C#List<string> list = new List<string> { "A", "B", "C" };
list.Clear();
Console.WriteLine(list.Count); // 0
この場合、listはnullにはなっていません。中身が空になったListとして、そのまま利用できます。
C#list.Add("D");
Console.WriteLine(list.Count); // 1
Clearは、Listを再利用したい場合に向いています。
3-2. null代入はListへの参照をなくす
nullを代入すると、その変数はListインスタンスを参照しなくなります。
C#List<string> list = new List<string> { "A", "B", "C" };
list = null;
この状態でClearを呼び出すと、NullReferenceExceptionが発生します。
C#list.Clear(); // NullReferenceException
null代入は、Listの中身を空にする操作ではありません。変数がListを指さなくなる操作です。
ただし、他の変数が同じListを参照している場合、そのList自体が消えるわけではありません。
C#List<string> list1 = new List<string> { "A", "B", "C" };
List<string> list2 = list1;
list1 = null;
Console.WriteLine(list2.Count); // 3
list1はnullになりますが、list2は同じListを参照し続けています。そのため、Listの中身は残っています。
3-3. new Listは新しいListを作り直す
new List<T>()を代入すると、新しいListインスタンスを作成して変数に代入します。
C#List<string> list = new List<string> { "A", "B", "C" };
list = new List<string>();
Console.WriteLine(list.Count); // 0
この場合、変数listは新しく作成された空のListを参照します。
Clearとの違いは、「同じListを空にする」のか、「新しいListに差し替える」のかです。
C#List<string> list1 = new List<string> { "A", "B", "C" };
List<string> list2 = list1;
list1 = new List<string>();
Console.WriteLine(list1.Count); // 0
Console.WriteLine(list2.Count); // 3
list1は新しい空のListを参照しますが、list2は元のListを参照したままです。そのため、list2には"A"、"B"、"C"が残っています。
一方、Clearの場合は同じListの中身を削除するため、同じListを参照している別の変数にも影響します。
C#List<string> list1 = new List<string> { "A", "B", "C" };
List<string> list2 = list1;
list1.Clear();
Console.WriteLine(list1.Count); // 0
Console.WriteLine(list2.Count); // 0
この違いは、実務でバグの原因になりやすいポイントです。
3-4. 使い分けの目安
Clear、null代入、new List<T>()は似ているように見えますが、目的が異なります。
Clearは、同じListを空にして再利用したいときに使います。
C#list.Clear();
null代入は、その変数からListへの参照を外したいときに使います。
C#list = null;
new List<T>()は、新しい空のListに差し替えたいときに使います。
C#list = new List<string>();
実務では、次のように考えるとよいでしょう。
同じListインスタンスを他の処理でも共有していて、その中身を空にしたいならClearが適しています。
C#sharedList.Clear();
現在の変数だけ新しいListに切り替えたいなら、new List<T>()が適しています。
C#items = new List<Item>();
Listが不要になり、参照を外したいだけならnull代入を使う場合もあります。ただし、C#ではガベージコレクションが自動的にメモリ管理を行うため、単にメモリを減らしたい目的だけで安易にnullを代入する必要はないケースも多いです。
4. List.Clearとメモリ解放の関係
4-1. ClearしてもCapacityは基本的に変わらない
List.Clear()を理解するうえで重要なのが、CountとCapacityの違いです。
Clearを呼び出すとCountは0になります。しかし、Capacityは基本的にそのまま残ります。Microsoftの公式ドキュメントでも、Clear後にCapacityは変わらず、容量をリセットしたい場合はTrimExcessを呼び出すか、Capacityプロパティを直接設定すると説明されています。Microsoft Learn+1
次の例を見てみましょう。
C#List<int> numbers = new List<int>();
for (int i = 0; i < 1000; i++)
{
numbers.Add(i);
}
Console.WriteLine($"Clear前 Count: {numbers.Count}");
Console.WriteLine($"Clear前 Capacity: {numbers.Capacity}");
numbers.Clear();
Console.WriteLine($"Clear後 Count: {numbers.Count}");
Console.WriteLine($"Clear後 Capacity: {numbers.Capacity}");
実行結果の例は次のようになります。
Clear前 Count: 1000
Clear前 Capacity: 1024
Clear後 Count: 0
Clear後 Capacity: 1024
実際のCapacityの値は環境や追加状況によって変わる可能性がありますが、ポイントはClear後もCapacityが残るということです。
つまり、Clearは「要素数を0にする」操作であり、「内部配列の容量を必ず0にする」操作ではありません。
4-2. CountとCapacityの違い
Countは、Listに現在入っている要素数です。
C#Console.WriteLine(list.Count);
一方、Capacityは、Listが内部的に確保している容量です。
C#Console.WriteLine(list.Capacity);
公式ドキュメントでは、Capacityはサイズ変更が必要になる前に格納できる要素数、Countは実際にListに含まれる要素数と説明されています。また、Capacityは常にCount以上になります。Microsoft Learn+1
たとえば、次のようなイメージです。
Count = 実際に入っている要素数
Capacity = 内部的に確保している入れ物の大きさ
List<T>は要素を追加するたびに毎回ぴったりのサイズでメモリを確保し直すわけではありません。ある程度の容量をまとめて確保し、必要に応じて拡張します。
そのため、次のようにCountとCapacityが異なることがあります。
C#List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
Console.WriteLine(list.Count); // 3
Console.WriteLine(list.Capacity); // 例: 4
Countは3ですが、Capacityは4など、Countより大きい値になる場合があります。
4-3. Clearだけではメモリ使用量がすぐ減らない理由
Clearを呼び出すと、Listの要素は削除されます。しかし、Listが内部的に確保している容量、つまりCapacityは基本的に残ります。
そのため、大量のデータを格納したListに対してClearを呼び出しても、アプリケーション全体のメモリ使用量がすぐに大きく減るとは限りません。
C#List<byte[]> buffers = new List<byte[]>();
for (int i = 0; i < 1000; i++)
{
buffers.Add(new byte[1024 * 1024]);
}
buffers.Clear();
この例では、ClearによってListの中にあった参照は削除されます。その結果、他に参照がなければ、各byte[]はガベージコレクションの対象になります。
ただし、ガベージコレクションがいつ実行されるかは.NETランタイムが判断します。そのため、Clearした瞬間に必ずメモリ使用量が目に見えて下がるとは限りません。
また、List自体の内部配列の容量は残るため、Listの入れ物部分のメモリは保持される可能性があります。
4-4. メモリを減らしたい場合はTrimExcessを使う
Clear後にListの余分な容量を減らしたい場合は、TrimExcessを使います。TrimExcessは、今後新しい要素を追加しない場合に、コレクションのメモリ上の余分な領域を最小化するために使えるメソッドです。Microsoft Learn+1
C#List<int> numbers = new List<int>();
for (int i = 0; i < 10000; i++)
{
numbers.Add(i);
}
Console.WriteLine(numbers.Capacity);
numbers.Clear();
numbers.TrimExcess();
Console.WriteLine(numbers.Count); // 0
Console.WriteLine(numbers.Capacity); // 容量が縮小される
TrimExcessを使うと、Listの不要な容量を削減できます。
ただし、TrimExcessを使えば常にパフォーマンスが良くなるわけではありません。容量を減らしたあとで再び大量の要素を追加すると、Listは再度容量を拡張する必要があります。
そのため、次のような場合はTrimExcessが有効です。
C#list.Clear();
list.TrimExcess();
たとえば、大量データを一度処理したあと、そのListをほとんど使わない場合です。
一方、同じListをすぐに再利用して大量の要素を追加する場合は、TrimExcessを使わずにClearだけにしたほうが効率的なことがあります。
4-5. 大量データを扱う場合の注意点
大量データを扱う場合、Clearとメモリ解放の違いを理解しておくことが重要です。
たとえば、数十万件、数百万件のデータをListに格納する処理では、ClearしてもCapacityが残るため、Listの内部配列が大きいままになる場合があります。
C#List<string> logs = new List<string>();
// 大量のログを追加
for (int i = 0; i < 1_000_000; i++)
{
logs.Add("log-" + i);
}
logs.Clear();
この時点でCountは0ですが、Capacityは大きいまま残る可能性があります。
そのListを再利用するなら、これは必ずしも悪いことではありません。次にまた大量の要素を追加するとき、すでに容量が確保されているため、再確保のコストを抑えられるからです。
しかし、もう大量データを扱わないのであれば、次のようにTrimExcessを使うことを検討します。
C#logs.Clear();
logs.TrimExcess();
また、Listそのものが不要であれば、新しいListに差し替える方法もあります。
C#logs = new List<string>();
大量データでは、「中身だけ空にしたいのか」「容量も減らしたいのか」「List自体を使い捨てたいのか」を意識して選ぶことが大切です。
5. List.Clearを使うときの注意点
5-1. Clear後にインデックスアクセスすると例外になる
Clear後のListは空です。そのため、インデックスで要素にアクセスしようとすると例外が発生します。
C#List<string> names = new List<string> { "Alice", "Bob" };
names.Clear();
Console.WriteLine(names[0]); // 例外
Clear後はCountが0なので、names[0]は存在しません。
安全にアクセスするには、事前にCountを確認します。
C#if (names.Count > 0)
{
Console.WriteLine(names[0]);
}
else
{
Console.WriteLine("要素はありません。");
}
ListをClearしたあとは、以前の要素が残っている前提で処理しないように注意しましょう。
5-2. foreach中にClearすると例外になる場合がある
foreachでListを列挙している最中に、そのListに対してClearを呼び出すと例外が発生する場合があります。
C#List<string> items = new List<string> { "A", "B", "C" };
foreach (string item in items)
{
Console.WriteLine(item);
items.Clear(); // 例外が発生する可能性がある
}
foreach中にListの内容を変更すると、列挙処理の整合性が崩れるためです。
全要素を削除したい場合は、foreachの外でClearを呼び出します。
C#foreach (string item in items)
{
Console.WriteLine(item);
}
items.Clear();
条件に一致する要素だけ削除したい場合は、RemoveAllを使う方法もあります。
C#items.RemoveAll(item => item == "B");
または、削除対象を別のListに集めてから、あとで削除する方法もあります。
C#List<string> removeTargets = new List<string>();
foreach (string item in items)
{
if (item == "B")
{
removeTargets.Add(item);
}
}
foreach (string target in removeTargets)
{
items.Remove(target);
}
foreach中に元のListを直接変更しないことがポイントです。
5-3. 別の変数が同じListを参照している場合の挙動
C#のListは参照型です。そのため、複数の変数が同じListインスタンスを参照していることがあります。
C#List<string> list1 = new List<string> { "A", "B", "C" };
List<string> list2 = list1;
list1.Clear();
Console.WriteLine(list2.Count); // 0
この場合、list1とlist2は同じListを参照しています。そのため、list1.Clear()を呼び出すと、list2から見ても中身は空になります。
これは、Clearが「変数を空にする」のではなく、「参照先のListインスタンスの中身を空にする」操作だからです。
実務では、メソッドにListを渡した先でClearされると、呼び出し元のListにも影響します。
C#void ResetList(List<string> values)
{
values.Clear();
}
List<string> data = new List<string> { "X", "Y", "Z" };
ResetList(data);
Console.WriteLine(data.Count); // 0
メソッド内でListを変更する場合は、呼び出し元にも影響することを意識しましょう。
5-4. オブジェクト自体がすぐに破棄されるとは限らない
Clearを呼び出すと、List内の要素への参照は削除されます。公式ドキュメントでも、ClearによりCountが0になり、コレクション内の要素から他のオブジェクトへの参照も解放されると説明されています。Microsoft Learn
ただし、それは「オブジェクトがその瞬間に必ず破棄される」という意味ではありません。
C#List<Person> people = new List<Person>();
Person person = new Person { Name = "Taro" };
people.Add(person);
people.Clear();
この例では、peopleからpersonへの参照は削除されます。しかし、変数personが同じオブジェクトを参照しているため、そのPersonオブジェクトはまだ利用できます。
C#Console.WriteLine(person.Name); // Taro
また、他に参照がなくなったオブジェクトはガベージコレクションの対象になりますが、実際にいつ回収されるかは.NETランタイムが判断します。
つまり、Clearは参照を外す操作であり、オブジェクトの即時破棄を保証する操作ではありません。
5-5. nullのListにClearを呼ぶと例外になる
List変数がnullの場合、Clearを呼び出すとNullReferenceExceptionが発生します。
C#List<string> items = null;
items.Clear(); // NullReferenceException
ClearはListインスタンスに対して呼び出すメソッドです。そのため、変数がnullだと呼び出せません。
安全に呼び出したい場合は、nullチェックを行います。
C#if (items != null)
{
items.Clear();
}
C# 6.0以降であれば、null条件演算子を使うこともできます。
C#items?.Clear();
この書き方なら、itemsがnullの場合は何も実行されず、例外も発生しません。
ただし、items?.Clear()を使うと、nullの場合に何も起こらないため、Listが必ず存在しているべき場面ではバグを見逃す可能性もあります。状況に応じて使い分けましょう。
6. Clear以外でListの要素を削除する方法
6-1. Removeで特定の要素を削除する
Removeは、Listから特定の要素を削除するメソッドです。公式ドキュメントでは、Remove(T)は指定したオブジェクトの最初の出現箇所をList<T>から削除すると説明されています。Microsoft Learn
C#List<string> fruits = new List<string>
{
"Apple",
"Banana",
"Orange"
};
fruits.Remove("Banana");
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
実行結果は次のとおりです。
Apple
Orange
Removeは、Listの中から指定した値を探し、最初に見つかった要素を削除します。
同じ値が複数ある場合、削除されるのは最初の1つです。
C#List<string> items = new List<string> { "A", "B", "B", "C" };
items.Remove("B");
Console.WriteLine(string.Join(", ", items)); // A, B, C
すべて削除したい場合は、RemoveAllを使うと便利です。
6-2. RemoveAtで指定位置の要素を削除する
RemoveAtは、指定したインデックス位置の要素を削除するメソッドです。公式ドキュメントでも、RemoveAt(Int32)は指定したインデックス位置にある要素を削除すると説明されています。Microsoft Learn
C#List<string> animals = new List<string>
{
"Dog",
"Cat",
"Bird"
};
animals.RemoveAt(1);
Console.WriteLine(string.Join(", ", animals)); // Dog, Bird
この例では、インデックス1にある"Cat"が削除されます。
C#のListは0始まりなので、インデックスは次のようになります。
0: Dog
1: Cat
2: Bird
存在しないインデックスを指定すると例外が発生します。
C#animals.RemoveAt(10); // 例外
安全に使うには、Countを確認します。
C#int index = 1;
if (index >= 0 && index < animals.Count)
{
animals.RemoveAt(index);
}
RemoveAtは、削除する位置が明確に決まっている場合に使います。
6-3. RemoveRangeで範囲指定して削除する
RemoveRangeは、指定した範囲の要素をまとめて削除するメソッドです。公式ドキュメントでは、RemoveRange(Int32, Int32)は指定したインデックスから指定個数の要素を削除すると説明されています。Microsoft Learn
C#List<int> numbers = new List<int>
{
10, 20, 30, 40, 50
};
numbers.RemoveRange(1, 3);
Console.WriteLine(string.Join(", ", numbers)); // 10, 50
この例では、インデックス1から3個の要素、つまり20、30、40が削除されます。
RemoveRangeの構文は次のとおりです。
C#list.RemoveRange(index, count);
indexは削除を開始する位置、countは削除する要素数です。
指定範囲がListの範囲を超えると例外が発生するため、事前に確認しておくと安全です。
C#int index = 1;
int count = 3;
if (index >= 0 && count >= 0 && index + count <= numbers.Count)
{
numbers.RemoveRange(index, count);
}
一部の範囲だけをまとめて消したい場合は、ClearではなくRemoveRangeを使います。
6-4. RemoveAllで条件に一致する要素を削除する
RemoveAllは、指定した条件に一致する要素をすべて削除するメソッドです。
C#List<int> numbers = new List<int>
{
1, 2, 3, 4, 5, 6
};
numbers.RemoveAll(n => n % 2 == 0);
Console.WriteLine(string.Join(", ", numbers)); // 1, 3, 5
この例では、偶数に一致する要素をすべて削除しています。
RemoveAllの構文は次のとおりです。
C#list.RemoveAll(条件);
条件には、Predicate<T>に相当するラムダ式を指定することが多いです。
文字列のListでも使えます。
C#List<string> names = new List<string>
{
"Taro",
"Hanako",
"Tom",
"Alice"
};
names.RemoveAll(name => name.StartsWith("T"));
Console.WriteLine(string.Join(", ", names)); // Hanako, Alice
この例では、"T"で始まる要素をすべて削除しています。
条件に一致する要素だけを削除したい場合は、RemoveAllが便利です。
6-5. 全削除ならClearが最もシンプル
Listの要素をすべて削除したい場合は、Clearが最もシンプルです。
C#items.Clear();
Remove、RemoveAt、RemoveRange、RemoveAllも要素削除に使えますが、それぞれ目的が異なります。
特定の値だけ削除したいならRemove。
C#items.Remove("A");
指定位置の要素を削除したいならRemoveAt。
C#items.RemoveAt(0);
範囲を指定して削除したいならRemoveRange。
C#items.RemoveRange(0, 3);
条件に一致する要素を削除したいならRemoveAll。
C#items.RemoveAll(x => x == "A");
そして、すべての要素を削除したいならClearです。
C#items.Clear();
「C#でListを空にしたい」「Listの中身を全部消したい」という場合は、まずClearを使うと覚えておくとよいでしょう。
7. 実務でのList.Clearの使いどころ
7-1. 一時的なデータを使い回す場合
実務では、一時的なデータをListに入れて処理し、処理が終わったら空にする場面があります。
C#List<string> tempMessages = new List<string>();
tempMessages.Add("エラー1");
tempMessages.Add("エラー2");
// メッセージを処理
foreach (string message in tempMessages)
{
Console.WriteLine(message);
}
// 処理後に空にする
tempMessages.Clear();
このような一時的なListは、処理ごとにClearして使い回すことがあります。
特に、同じ処理を何度も繰り返す場合、毎回new List<T>()で作り直すより、Clearして再利用するほうが自然なケースもあります。
C#List<string> workList = new List<string>();
void Process()
{
workList.Clear();
workList.Add("data1");
workList.Add("data2");
// workListを使った処理
}
ただし、Listをフィールドとして使い回す場合は、前回のデータが残らないように、処理の開始時または終了時にClearする場所を明確にしておくことが大切です。
7-2. ループ処理でListを再利用する場合
ループ処理の中で、Listを一時バッファとして使うことがあります。
C#List<int> buffer = new List<int>();
for (int i = 0; i < 10; i++)
{
buffer.Clear();
buffer.Add(i);
buffer.Add(i * 2);
buffer.Add(i * 3);
Console.WriteLine(string.Join(", ", buffer));
}
この例では、ループのたびにbuffer.Clear()で中身を空にしてから、新しい値を追加しています。
毎回新しいListを作る場合は、次のようになります。
C#for (int i = 0; i < 10; i++)
{
List<int> buffer = new List<int>();
buffer.Add(i);
buffer.Add(i * 2);
buffer.Add(i * 3);
Console.WriteLine(string.Join(", ", buffer));
}
どちらがよいかは状況によります。
小さなListであれば、可読性を重視してループ内でnew List<T>()を作るほうがわかりやすい場合もあります。
一方、頻繁に呼ばれる処理や、大きなListを繰り返し使う処理では、Clearで再利用することを検討できます。
7-3. キャッシュやバッファをリセットする場合
Clearは、キャッシュやバッファをリセットする場面でも使われます。
C#private readonly List<string> _cache = new List<string>();
public void AddCache(string value)
{
_cache.Add(value);
}
public void ResetCache()
{
_cache.Clear();
}
このように、内部的に保持しているListの中身をリセットしたい場合、Clearは直感的です。
また、読み込んだデータを一時的にためるバッファにも使えます。
C#List<byte> buffer = new List<byte>();
// データを追加
buffer.Add(0x01);
buffer.Add(0x02);
buffer.Add(0x03);
// 送信後にリセット
buffer.Clear();
ただし、キャッシュをClearする場合は、そのキャッシュを参照している他の処理がないか注意が必要です。
たとえば、外部にListそのものを返している場合、その参照先も空になります。
C#private List<string> _items = new List<string>();
public List<string> GetItems()
{
return _items;
}
public void ClearItems()
{
_items.Clear();
}
GetItems()で受け取った側も同じListを参照しているため、ClearItems()が呼ばれると中身が空になります。
必要に応じて、外部にはコピーを返す設計にすることも検討します。
C#public List<string> GetItems()
{
return new List<string>(_items);
}
7-4. new ListとClearのどちらを選ぶべきか
new List<T>()とClearのどちらを使うべきかは、Listを共有しているか、再利用したいか、容量を残したいかで判断します。
同じListインスタンスを保持したまま中身だけ空にしたい場合は、Clearが適しています。
C#items.Clear();
新しいListとして扱いたい場合は、new List<T>()が適しています。
C#items = new List<string>();
たとえば、フィールドに保持しているListを他のオブジェクトも参照している場合、new List<T>()で差し替えると、自分の変数だけが新しいListを参照します。既存の参照先には古いListが残ります。
C#List<string> list1 = new List<string> { "A", "B" };
List<string> list2 = list1;
list1 = new List<string>();
Console.WriteLine(list1.Count); // 0
Console.WriteLine(list2.Count); // 2
一方、Clearの場合は同じListの中身が空になります。
C#List<string> list1 = new List<string> { "A", "B" };
List<string> list2 = list1;
list1.Clear();
Console.WriteLine(list1.Count); // 0
Console.WriteLine(list2.Count); // 0
Listを共有しているなら、この違いを理解しておく必要があります。
また、パフォーマンス面では、Clearは既存の容量を残すため、同じ程度の要素を再び追加する処理では効率的な場合があります。逆に、もうその容量が不要なら、new List<T>()やTrimExcessを検討します。
8. List.Clearに関するよくある質問
8-1. ClearするとListはnullになる?
ClearしてもListはnullになりません。
C#List<string> list = new List<string> { "A", "B" };
list.Clear();
Console.WriteLine(list == null); // False
Console.WriteLine(list.Count); // 0
Clearは、Listの中身を空にするだけです。List変数は引き続きListインスタンスを参照しています。
そのため、Clear後もAddできます。
C#list.Add("C");
Console.WriteLine(list.Count); // 1
Listをnullにしたい場合は、明示的にnullを代入する必要があります。
C#list = null;
ただし、nullにすると、その後にlist.Clear()やlist.Add()を呼び出したときに例外が発生するため注意しましょう。
8-2. ClearするとCapacityも0になる?
通常、ClearしてもCapacityは0になりません。
C#List<int> list = new List<int>();
for (int i = 0; i < 100; i++)
{
list.Add(i);
}
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
list.Clear();
Console.WriteLine(list.Count); // 0
Console.WriteLine(list.Capacity); // 多くの場合、0ではない
Clearで0になるのはCountです。Capacityは、Listが内部的に確保している容量なので、基本的には保持されます。
容量も減らしたい場合は、TrimExcessを使います。
C#list.Clear();
list.TrimExcess();
ただし、TrimExcess後にまた多くの要素を追加する場合、再び容量確保が必要になります。そのListを再利用する予定があるなら、Clearだけにしておくほうがよい場合もあります。
8-3. Clear後にメモリを完全に解放できる?
Clearだけでメモリが完全に解放されるとは限りません。
Clearを呼び出すと、List内の要素への参照は削除されます。参照されなくなったオブジェクトは、ガベージコレクションの対象になります。
しかし、ガベージコレクションがいつ実行されるかは.NETランタイムが決めます。そのため、Clearした直後に必ずメモリ使用量が下がるわけではありません。
また、ListのCapacityは基本的に残ります。
メモリ使用量を減らしたい場合は、次のような方法があります。
C#list.Clear();
list.TrimExcess();
または、Listを使い捨てる設計にして、新しいListに差し替える方法もあります。
C#list = new List<string>();
ただし、通常のアプリケーションでは、ガベージコレクションに任せるほうが自然なケースも多いです。必要以上にメモリ解放を意識して複雑なコードにするより、まずは読みやすく安全なコードを優先しましょう。
8-4. ClearとDisposeは同じ?
ClearとDisposeは同じではありません。
Clearは、Listの中身を削除するメソッドです。
C#list.Clear();
一方、Disposeは、ファイル、ストリーム、データベース接続など、明示的に解放が必要なリソースを片付けるために使われるメソッドです。
通常のList<T>自体はIDisposableを実装していないため、次のようなコードは書けません。
C#list.Dispose(); // List<T>には通常Disposeはない
ただし、Listの中にIDisposableを実装したオブジェクトが入っている場合は注意が必要です。
C#List<IDisposable> resources = new List<IDisposable>();
// resourcesにIDisposableなオブジェクトが入っている場合
foreach (IDisposable resource in resources)
{
resource.Dispose();
}
resources.Clear();
ClearはListから要素を削除するだけで、要素のDisposeを自動で呼び出すわけではありません。
そのため、List内の各要素が明示的な解放を必要とする場合は、先にDisposeを呼び出してからClearする設計が必要です。
8-5. Clearは配列にも使える?
ClearはList<T>のメソッドなので、通常の配列には同じようには使えません。
C#int[] numbers = { 1, 2, 3 };
numbers.Clear(); // 配列にはこのようなClearメソッドはない
配列の要素を初期値に戻したい場合は、Array.Clearを使います。
C#int[] numbers = { 1, 2, 3 };
Array.Clear(numbers, 0, numbers.Length);
Console.WriteLine(string.Join(", ", numbers)); // 0, 0, 0
文字列配列の場合は、要素がnullになります。
C#string[] names = { "A", "B", "C" };
Array.Clear(names, 0, names.Length);
Console.WriteLine(names[0] == null); // True
Listと配列では、削除の意味が少し異なります。
List<T>.Clear()は、Listの要素数であるCountを0にします。
C#List<int> list = new List<int> { 1, 2, 3 };
list.Clear();
Console.WriteLine(list.Count); // 0
一方、配列は固定長なので、要素数そのものは変わりません。Array.Clearは、配列の各要素を初期値に戻す操作です。
C#int[] array = { 1, 2, 3 };
Array.Clear(array, 0, array.Length);
Console.WriteLine(array.Length); // 3
「要素数を0にしたい」ならList<T>とClearを使い、「固定長の配列の中身を初期化したい」ならArray.Clearを使います。
まとめ
C#でListの中身をすべて削除したい場合は、List.Clear()を使います。
C#list.Clear();
Clearを呼び出すと、List内のすべての要素が削除され、Countは0になります。一方で、List変数そのものはnullにならず、同じListインスタンスをそのまま再利用できます。
C#List<string> list = new List<string> { "A", "B", "C" };
list.Clear();
Console.WriteLine(list.Count); // 0
list.Add("D");
Console.WriteLine(list.Count); // 1
Clearとnull代入、new List<T>()は目的が異なります。
Clearは、同じListの中身だけを空にする操作です。
C#list.Clear();
null代入は、変数からListへの参照を外す操作です。
C#list = null;
new List<T>()は、新しいListに差し替える操作です。
C#list = new List<string>();
また、ClearしてもCapacityは基本的に変わりません。メモリ使用量を減らしたい場合は、必要に応じてTrimExcessを検討します。
C#list.Clear();
list.TrimExcess();
ただし、Listをすぐに再利用する場合は、Capacityを残しておいたほうが効率的なこともあります。
C#のListで全要素を削除したいなら、基本はClearです。特定の要素だけ削除するならRemove、位置で削除するならRemoveAt、範囲で削除するならRemoveRange、条件で削除するならRemoveAllを使い分けましょう。

