C# ListのRemoveAtで要素を削除する方法|インデックス指定・エラー回避・実例を解説

はじめに

C#でList<T>の要素を削除したいとき、よく使われるメソッドのひとつがRemoveAtです。

RemoveAtは、指定したインデックス位置の要素を削除するためのメソッドです。たとえば、リストの先頭を削除したい場合はRemoveAt(0)、末尾を削除したい場合はRemoveAt(list.Count - 1)のように書きます。

ただし、RemoveAtは便利な一方で、存在しないインデックスを指定するとArgumentOutOfRangeExceptionが発生します。また、ループ中に使う場合は要素の位置がずれる点にも注意が必要です。

この記事では、c# list removeatの基本から、エラー回避、条件に合う要素の削除、ループ中の注意点、似た削除メソッドとの違い、実践的なコード例まで解説します。

1. C#のList.RemoveAtとは?インデックス指定で要素を削除する基本

1-1. RemoveAtメソッドの役割

RemoveAtは、C#のList<T>に用意されているメソッドで、指定したインデックスにある要素を削除するために使います。

C#
List<string> fruits = new List<string> { "Apple", "Banana", "Orange" };

fruits.RemoveAt(1);

Console.WriteLine(string.Join(", ", fruits));

実行結果は次のようになります。

Apple, Orange

インデックス1にある"Banana"が削除されました。

List<T>は要素を順番に保持するコレクションです。そのため、各要素には先頭から順にインデックスが割り当てられます。RemoveAtは、そのインデックスを指定して要素を削除します。

1-2. RemoveAtの基本構文

RemoveAtの基本構文は次のとおりです。

C#
list.RemoveAt(index);

indexには、削除したい要素の位置をint型で指定します。

C#
List<int> numbers = new List<int> { 10, 20, 30, 40 };

numbers.RemoveAt(2);

Console.WriteLine(string.Join(", ", numbers));

実行結果は次のとおりです。

10, 20, 40

この例では、インデックス2にある30が削除されています。

C#のインデックスは0から始まるため、次のような対応になります。

インデックス要素
010
120
230
340

そのため、RemoveAt(2)は3番目の要素を削除する処理になります。

1-3. RemoveAtで削除できる対象は「要素」ではなく「位置」

RemoveAtで指定するのは、削除したい値そのものではなく、削除したい位置です。

たとえば、次のコードを見てみましょう。

C#
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };

names.RemoveAt(1);

この場合、"Bob"という値を指定しているのではなく、インデックス1の位置を指定しています。

つまり、RemoveAtは次のような考え方です。

「Bobを削除する」ではなく
「1番目の位置にある要素を削除する」

値を指定して削除したい場合は、RemoveAtではなくRemoveを使います。

C#
names.Remove("Bob");

一方で、位置がわかっている場合はRemoveAtが適しています。

1-4. RemoveAtを使う場面

RemoveAtは、次のような場面でよく使われます。

場面
先頭の要素を削除したいlist.RemoveAt(0)
末尾の要素を削除したいlist.RemoveAt(list.Count - 1)
ユーザーが選択した行を削除したいlist.RemoveAt(selectedIndex)
条件に一致した要素の位置を調べて削除したいFindIndexと組み合わせる
複数要素を後ろから順に削除したいfor文で逆順に処理する

特に、画面上の一覧やメニュー、テーブル、配列のようなデータから「何番目の要素を削除する」という処理ではRemoveAtが使いやすいです。

2. List.RemoveAtの基本的な使い方

2-1. 指定したインデックスの要素を削除するサンプル

まずは、もっとも基本的な使い方です。

C#
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
List<string> colors = new List<string>
{
"Red",
"Green",
"Blue",
"Yellow"
};

colors.RemoveAt(2);

Console.WriteLine(string.Join(", ", colors));
}
}

実行結果は次のとおりです。

Red, Green, Yellow

インデックス2にある"Blue"が削除されました。

削除前のリストは次の状態です。

インデックス要素
0Red
1Green
2Blue
3Yellow

RemoveAt(2)を実行すると、Blueが削除され、後ろにあったYellowが前に詰められます。

削除後は次の状態になります。

インデックス要素
0Red
1Green
2Yellow

2-2. 先頭の要素を削除する方法

Listの先頭要素を削除するには、インデックス0を指定します。

C#
List<string> tasks = new List<string>
{
"メール確認",
"資料作成",
"会議"
};

tasks.RemoveAt(0);

Console.WriteLine(string.Join(", ", tasks));

実行結果は次のとおりです。

資料作成, 会議

先頭の"メール確認"が削除されました。

ただし、空のListに対してRemoveAt(0)を実行するとエラーになります。先頭を削除する場合でも、事前にCountを確認しておくと安全です。

C#
if (tasks.Count > 0)
{
tasks.RemoveAt(0);
}

2-3. 末尾の要素を削除する方法

Listの末尾要素を削除するには、Count - 1を指定します。

C#
List<string> fruits = new List<string>
{
"Apple",
"Banana",
"Orange"
};

fruits.RemoveAt(fruits.Count - 1);

Console.WriteLine(string.Join(", ", fruits));

実行結果は次のとおりです。

Apple, Banana

fruits.Count3なので、末尾のインデックスは2です。

C#
fruits.RemoveAt(2);

と同じ意味になります。

C#のListでは、最後の要素のインデックスは常に次の式で求められます。

C#
list.Count - 1

ただし、Listが空の場合、Count - 1-1になります。RemoveAt(-1)はエラーになるため、末尾を削除する場合も必ず要素数を確認しましょう。

C#
if (fruits.Count > 0)
{
fruits.RemoveAt(fruits.Count - 1);
}

2-4. 削除後にListの中身がどう変わるか

RemoveAtで要素を削除すると、その位置より後ろにある要素は前に詰められます。

C#
List<int> numbers = new List<int> { 100, 200, 300, 400, 500 };

numbers.RemoveAt(1);

Console.WriteLine(string.Join(", ", numbers));

実行結果は次のとおりです。

100, 300, 400, 500

削除前は次の状態です。

インデックス要素
0100
1200
2300
3400
4500

RemoveAt(1)200を削除すると、300400500が前に詰められます。

削除後は次の状態です。

インデックス要素
0100
1300
2400
3500

このように、RemoveAt実行後はインデックスが変わります。削除前に覚えていたインデックスをそのまま使うと、意図しない要素を参照する可能性があります。

3. RemoveAtでよく起きるエラーと回避方法

3-1. ArgumentOutOfRangeExceptionが発生する原因

RemoveAtで最もよく発生するエラーがArgumentOutOfRangeExceptionです。

これは、指定したインデックスがListの範囲外だった場合に発生します。

C#
List<string> items = new List<string> { "A", "B", "C" };

items.RemoveAt(3);

このコードはエラーになります。

Listの要素数は3ですが、有効なインデックスは012です。インデックス3は存在しません。

有効なインデックスの範囲は次のとおりです。

0以上、Count - 1以下

つまり、RemoveAtで指定できるインデックスは次の条件を満たす必要があります。

C#
index >= 0 && index < list.Count

3-2. インデックスが0から始まる点に注意

C#のListのインデックスは0から始まります。

C#
List<string> animals = new List<string>
{
"Dog",
"Cat",
"Bird"
};

このListのインデックスは次のようになります。

順番インデックス要素
1番目0Dog
2番目1Cat
3番目2Bird

3番目の要素を削除したい場合は、RemoveAt(3)ではなくRemoveAt(2)です。

C#
animals.RemoveAt(2);

RemoveAt(3)と書くと、存在しない4番目の位置を指定したことになり、ArgumentOutOfRangeExceptionが発生します。

初心者がc# list removeatでつまずきやすい原因のひとつが、このインデックスの数え方です。

3-3. Countを使って範囲外アクセスを防ぐ方法

RemoveAtを安全に使うには、削除前にCountを使ってインデックスが範囲内か確認します。

C#
List<string> items = new List<string> { "A", "B", "C" };

int index = 1;

if (index >= 0 && index < items.Count)
{
items.RemoveAt(index);
}

Console.WriteLine(string.Join(", ", items));

実行結果は次のとおりです。

A, C

このチェックを入れておけば、インデックスが範囲外の場合は削除処理を実行しないため、エラーを防げます。

たとえば、次のようにインデックスが存在しない場合でも安全です。

C#
int index = 10;

if (index >= 0 && index < items.Count)
{
items.RemoveAt(index);
}
else
{
Console.WriteLine("指定されたインデックスは存在しません。");
}

RemoveAtを使うときは、次の条件をセットで覚えておくと便利です。

C#
if (index >= 0 && index < list.Count)
{
list.RemoveAt(index);
}

3-4. 空のListにRemoveAtを使うとどうなるか

空のListに対してRemoveAtを使うと、必ずエラーになります。

C#
List<string> items = new List<string>();

items.RemoveAt(0);

このコードは、ArgumentOutOfRangeExceptionになります。

空のListはCount0です。そのため、有効なインデックスはひとつも存在しません。

C#
Console.WriteLine(items.Count);

実行結果は次のとおりです。

0

先頭要素を削除したい場合でも、Listが空でないか確認する必要があります。

C#
if (items.Count > 0)
{
items.RemoveAt(0);
}

末尾要素を削除する場合も同じです。

C#
if (items.Count > 0)
{
items.RemoveAt(items.Count - 1);
}

3-5. 安全に削除するためのチェック処理

実務では、削除処理をメソッド化しておくと便利です。

C#
static bool RemoveAtSafe<T>(List<T> list, int index)
{
if (list == null)
{
return false;
}

if (index < 0 || index >= list.Count)
{
return false;
}

list.RemoveAt(index);
return true;
}

使用例は次のとおりです。

C#
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };

bool removed = RemoveAtSafe(names, 1);

if (removed)
{
Console.WriteLine("削除しました。");
}
else
{
Console.WriteLine("削除できませんでした。");
}

Console.WriteLine(string.Join(", ", names));

実行結果は次のとおりです。

削除しました。
Alice, Charlie

このように、RemoveAtを直接呼び出す前に、Listがnullでないか、インデックスが範囲内かを確認すると安全です。

4. 条件に合う要素をRemoveAtで削除する方法

4-1. IndexOfで要素の位置を取得して削除する

RemoveAtはインデックスを指定して削除するメソッドです。そのため、値をもとに削除したい場合は、まずIndexOfで位置を取得します。

C#
List<string> fruits = new List<string>
{
"Apple",
"Banana",
"Orange"
};

int index = fruits.IndexOf("Banana");

if (index >= 0)
{
fruits.RemoveAt(index);
}

Console.WriteLine(string.Join(", ", fruits));

実行結果は次のとおりです。

Apple, Orange

IndexOfは、指定した要素が見つかった場合にそのインデックスを返します。見つからない場合は-1を返します。

そのため、RemoveAtを呼び出す前にindex >= 0を確認しています。

C#
if (index >= 0)
{
fruits.RemoveAt(index);
}

このチェックをしないと、要素が見つからなかったときにRemoveAt(-1)となり、エラーになります。

4-2. FindIndexで条件に合う要素を削除する

値の完全一致ではなく、条件に合う要素を削除したい場合はFindIndexを使います。

C#
List<int> scores = new List<int> { 80, 95, 60, 40, 70 };

int index = scores.FindIndex(score => score < 50);

if (index >= 0)
{
scores.RemoveAt(index);
}

Console.WriteLine(string.Join(", ", scores));

実行結果は次のとおりです。

80, 95, 60, 70

この例では、50未満の最初の要素である40を削除しています。

FindIndexは、条件に一致する最初の要素のインデックスを返します。条件に一致する要素がない場合は-1を返します。

文字列でも同じように使えます。

C#
List<string> names = new List<string>
{
"Alice",
"Bob",
"Charlie",
"David"
};

int index = names.FindIndex(name => name.StartsWith("C"));

if (index >= 0)
{
names.RemoveAt(index);
}

Console.WriteLine(string.Join(", ", names));

実行結果は次のとおりです。

Alice, Bob, David

4-3. 条件に一致しない場合のエラー回避

FindIndexで条件に一致する要素がない場合、戻り値は-1です。

C#
List<int> numbers = new List<int> { 10, 20, 30 };

int index = numbers.FindIndex(n => n > 100);

numbers.RemoveAt(index);

このコードはエラーになります。n > 100に一致する要素がないため、index-1になります。

安全に書くには、次のようにチェックします。

C#
List<int> numbers = new List<int> { 10, 20, 30 };

int index = numbers.FindIndex(n => n > 100);

if (index >= 0)
{
numbers.RemoveAt(index);
}
else
{
Console.WriteLine("条件に一致する要素はありません。");
}

IndexOfFindIndexRemoveAtを組み合わせる場合は、必ず-1チェックを入れましょう。

4-4. 複数条件で削除位置を探す例

FindIndexでは、複数の条件を組み合わせることもできます。

C#
class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public bool IsActive { get; set; }
}

次の例では、Id2で、かつIsActivefalseのユーザーを削除します。

C#
List<User> users = new List<User>
{
new User { Id = 1, Name = "Alice", IsActive = true },
new User { Id = 2, Name = "Bob", IsActive = false },
new User { Id = 3, Name = "Charlie", IsActive = true }
};

int index = users.FindIndex(user => user.Id == 2 && user.IsActive == false);

if (index >= 0)
{
users.RemoveAt(index);
}

foreach (User user in users)
{
Console.WriteLine($"{user.Id}: {user.Name}, Active={user.IsActive}");
}

実行結果は次のとおりです。

1: Alice, Active=True
3: Charlie, Active=True

RemoveAt自体は条件を指定できませんが、FindIndexと組み合わせることで、条件に一致した要素をインデックス指定で削除できます。

5. ループ処理中にRemoveAtを使うときの注意点

5-1. foreach中にRemoveAtを使うとエラーになる理由

foreachでListを回している最中にRemoveAtで要素を削除すると、エラーになることがあります。

C#
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

foreach (int number in numbers)
{
if (number % 2 == 0)
{
int index = numbers.IndexOf(number);
numbers.RemoveAt(index);
}
}

このコードは、実行時にInvalidOperationExceptionが発生します。

理由は、foreachがListを列挙している最中に、そのListの内容を変更しているためです。

foreachはコレクションの状態が変わらないことを前提に処理を進めます。その途中でRemoveAtによって要素数や並び順が変わると、列挙処理が継続できなくなります。

そのため、Listから要素を削除しながらループする場合は、foreachではなくfor文を使うのが基本です。

5-2. for文で前から削除すると要素がずれる問題

for文を使えば、ループ中にRemoveAtを呼び出すことはできます。ただし、前から順に削除すると要素がずれる問題があります。

C#
List<int> numbers = new List<int> { 1, 2, 4, 5 };

for (int i = 0; i < numbers.Count; i++)
{
if (numbers[i] % 2 == 0)
{
numbers.RemoveAt(i);
}
}

Console.WriteLine(string.Join(", ", numbers));

このコードは、偶数をすべて削除したい意図で書いています。しかし、削除後に要素が前に詰められるため、次の要素を飛ばしてしまう場合があります。

削除前のListは次のとおりです。

1, 2, 4, 5

i = 12を削除すると、4がインデックス1に移動します。しかし、その後i2に進むため、移動してきた4をチェックしないまま通過してしまいます。

このように、前から削除すると、連続する削除対象を見落とす可能性があります。

5-3. 後ろからループして安全に削除する方法

ループ中にRemoveAtで複数要素を削除する場合は、後ろから前に向かって処理するのが安全です。

C#
List<int> numbers = new List<int> { 1, 2, 4, 5 };

for (int i = numbers.Count - 1; i >= 0; i--)
{
if (numbers[i] % 2 == 0)
{
numbers.RemoveAt(i);
}
}

Console.WriteLine(string.Join(", ", numbers));

実行結果は次のとおりです。

1, 5

後ろから削除すれば、削除した位置より前のインデックスには影響しません。

たとえば、インデックス24を削除しても、インデックス01の要素はそのままです。そのため、ループ処理に影響が出にくくなります。

複数要素をRemoveAtで削除する場合は、次の形を覚えておくと便利です。

C#
for (int i = list.Count - 1; i >= 0; i--)
{
if (条件)
{
list.RemoveAt(i);
}
}

5-4. 条件に合う複数要素をRemoveAtで削除する例

次の例では、在庫が0の商品をListから削除します。

C#
class Product
{
public string Name { get; set; } = "";
public int Stock { get; set; }
}
C#
List<Product> products = new List<Product>
{
new Product { Name = "Keyboard", Stock = 5 },
new Product { Name = "Mouse", Stock = 0 },
new Product { Name = "Monitor", Stock = 3 },
new Product { Name = "Cable", Stock = 0 }
};

for (int i = products.Count - 1; i >= 0; i--)
{
if (products[i].Stock == 0)
{
products.RemoveAt(i);
}
}

foreach (Product product in products)
{
Console.WriteLine($"{product.Name}: {product.Stock}");
}

実行結果は次のとおりです。

Keyboard: 5
Monitor: 3

在庫が0だったMouseCableが削除されています。

このように、条件に合う複数要素をRemoveAtで削除する場合は、後ろからループする方法が安全です。

6. RemoveAtと似た削除メソッドの違い

6-1. RemoveAtとRemoveの違い

RemoveAtRemoveはどちらもListから要素を削除するメソッドですが、指定するものが異なります。

メソッド指定するもの削除対象
RemoveAtインデックス指定位置の要素
Remove指定した値に一致する最初の要素

例を見てみましょう。

C#
List<string> fruits = new List<string>
{
"Apple",
"Banana",
"Orange"
};

fruits.RemoveAt(1);

この場合は、インデックス1"Banana"を削除します。

一方、Removeは値を指定します。

C#
fruits.Remove("Banana");

この場合は、"Banana"という値に一致する最初の要素を削除します。

位置で削除したいならRemoveAt、値で削除したいならRemoveを使います。

6-2. RemoveAtと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

偶数がすべて削除されました。

RemoveAtは1回の呼び出しで1つの位置だけを削除します。一方、RemoveAllは条件に合う要素をまとめて削除できます。

メソッド特徴
RemoveAt指定したインデックスの要素を1つ削除
RemoveAll条件に一致する要素をすべて削除

条件に合う複数要素を削除するなら、RemoveAllの方が簡潔です。

ただし、「特定の位置にある要素を削除したい」場合はRemoveAtを使います。

6-3. RemoveAtとRemoveRangeの違い

RemoveRangeは、指定した範囲の要素をまとめて削除するメソッドです。

C#
List<string> items = new List<string>
{
"A",
"B",
"C",
"D",
"E"
};

items.RemoveRange(1, 3);

Console.WriteLine(string.Join(", ", items));

実行結果は次のとおりです。

A, E

RemoveRange(1, 3)は、インデックス1から3個の要素を削除するという意味です。

削除された要素は次の3つです。

B, C, D

RemoveAtとの違いは次のとおりです。

メソッド削除内容
RemoveAt(index)指定した位置の要素を1つ削除
RemoveRange(index, count)指定した位置から指定個数分を削除

連続した複数要素を削除したい場合は、RemoveRangeが便利です。

6-4. RemoveAtとClearの違い

Clearは、List内のすべての要素を削除するメソッドです。

C#
List<int> numbers = new List<int> { 1, 2, 3 };

numbers.Clear();

Console.WriteLine(numbers.Count);

実行結果は次のとおりです。

0

RemoveAtは指定した位置の要素を1つだけ削除しますが、Clearはすべての要素を削除します。

メソッド削除内容
RemoveAt指定位置の要素を1つ削除
Clearすべての要素を削除

Listを空にしたい場合はClearを使い、一部だけ削除したい場合はRemoveAtRemoveを使います。

6-5. 目的別の使い分け早見表

C#のListには複数の削除メソッドがあります。目的に応じて使い分けると、コードがわかりやすくなります。

やりたいこと使うメソッド
指定位置の要素を削除したいRemoveAtlist.RemoveAt(2)
指定した値を削除したいRemovelist.Remove("A")
条件に一致する要素をすべて削除したいRemoveAlllist.RemoveAll(x => x < 0)
指定範囲をまとめて削除したいRemoveRangelist.RemoveRange(1, 3)
すべて削除したいClearlist.Clear()

c# list removeatを使うべきなのは、「削除したい位置がわかっている場合」です。

値や条件をもとに削除したい場合は、RemoveRemoveAllの方が自然なケースもあります。

7. RemoveAtの実践的なコード例

7-1. ユーザー一覧から指定位置のデータを削除する例

画面上の一覧でユーザーが選択した行を削除するような場面では、選択されたインデックスを使ってRemoveAtできます。

C#
class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
C#
List<User> users = new List<User>
{
new User { Id = 1, Name = "Alice" },
new User { Id = 2, Name = "Bob" },
new User { Id = 3, Name = "Charlie" }
};

int selectedIndex = 1;

if (selectedIndex >= 0 && selectedIndex < users.Count)
{
users.RemoveAt(selectedIndex);
}

foreach (User user in users)
{
Console.WriteLine($"{user.Id}: {user.Name}");
}

実行結果は次のとおりです。

1: Alice
3: Charlie

selectedIndex1なので、2番目のユーザーであるBobが削除されました。

実務では、ユーザーが選択した位置が必ず正しいとは限りません。そのため、RemoveAtの前に範囲チェックを入れるのが重要です。

7-2. 商品リストから末尾の商品を削除する例

最後に追加した商品を取り消すような処理では、末尾要素を削除することがあります。

C#
List<string> products = new List<string>
{
"Laptop",
"Mouse",
"Keyboard"
};

if (products.Count > 0)
{
products.RemoveAt(products.Count - 1);
}

Console.WriteLine(string.Join(", ", products));

実行結果は次のとおりです。

Laptop, Mouse

末尾の"Keyboard"が削除されました。

末尾を削除する場合は、必ずCount > 0を確認してからCount - 1を使いましょう。

C#
if (products.Count > 0)
{
products.RemoveAt(products.Count - 1);
}

7-3. 条件に一致した最初の要素を削除する例

次の例では、価格が1000円以上の最初の商品を削除します。

C#
class Product
{
public string Name { get; set; } = "";
public int Price { get; set; }
}
C#
List<Product> products = new List<Product>
{
new Product { Name = "Pen", Price = 100 },
new Product { Name = "Notebook", Price = 300 },
new Product { Name = "Bag", Price = 2500 },
new Product { Name = "Desk", Price = 8000 }
};

int index = products.FindIndex(product => product.Price >= 1000);

if (index >= 0)
{
products.RemoveAt(index);
}

foreach (Product product in products)
{
Console.WriteLine($"{product.Name}: {product.Price}円");
}

実行結果は次のとおりです。

Pen: 100円
Notebook: 300円
Desk: 8000円

Price >= 1000に最初に一致したBagが削除されました。

FindIndexRemoveAtを組み合わせると、「条件に一致する最初の要素を削除する」処理をわかりやすく書けます。

7-4. 条件に一致した複数要素を削除する例

条件に一致する複数要素をRemoveAtで削除する場合は、後ろからループします。

C#
List<int> scores = new List<int> { 95, 40, 80, 30, 70 };

for (int i = scores.Count - 1; i >= 0; i--)
{
if (scores[i] < 60)
{
scores.RemoveAt(i);
}
}

Console.WriteLine(string.Join(", ", scores));

実行結果は次のとおりです。

95, 80, 70

60未満の4030が削除されました。

同じ処理はRemoveAllでも書けます。

C#
scores.RemoveAll(score => score < 60);

ただし、削除時にインデックスを使った追加処理を行いたい場合や、削除位置を明確に扱いたい場合はRemoveAtを使うことがあります。

7-5. 削除前後のListを表示して確認する例

RemoveAtの動きを確認するには、削除前後のListを表示するとわかりやすいです。

C#
List<string> languages = new List<string>
{
"C#",
"Java",
"Python",
"JavaScript"
};

Console.WriteLine("削除前:");
Console.WriteLine(string.Join(", ", languages));

int index = 1;

if (index >= 0 && index < languages.Count)
{
Console.WriteLine($"削除する要素: {languages[index]}");
languages.RemoveAt(index);
}

Console.WriteLine("削除後:");
Console.WriteLine(string.Join(", ", languages));

実行結果は次のとおりです。

削除前:
C#, Java, Python, JavaScript
削除する要素: Java
削除後:
C#, Python, JavaScript

RemoveAtは削除した要素を返さないため、削除する要素を表示したい場合は、削除前にlanguages[index]で取得しておきます。

8. RemoveAtを使う際のパフォーマンスと注意点

8-1. RemoveAt後に後続要素が前に詰められる仕組み

List<T>は内部的に配列のような連続した領域で要素を管理しています。

そのため、途中の要素をRemoveAtで削除すると、後ろにある要素を前に移動する必要があります。

C#
List<int> numbers = new List<int> { 10, 20, 30, 40, 50 };

numbers.RemoveAt(1);

削除前は次の状態です。

10, 20, 30, 40, 50

20を削除すると、後ろの要素が前に詰められます。

10, 30, 40, 50

このとき、304050の位置が変わります。

先頭に近い要素を削除するほど、移動する要素が多くなります。逆に、末尾の要素を削除する場合は、後続要素の移動がないため比較的軽い処理になります。

8-2. 大量データでRemoveAtを多用すると遅くなる理由

大量のデータに対してRemoveAtを何度も使うと、処理が遅くなる場合があります。

特に、Listの先頭や中間から何度も削除する場合は注意が必要です。

C#
for (int i = 0; i < 1000; i++)
{
list.RemoveAt(0);
}

このように先頭を何度も削除すると、削除のたびに後続要素が前に移動します。

要素数が多いListでこの処理を繰り返すと、移動コストが積み重なり、パフォーマンスが悪くなる可能性があります。

たとえば、キューのように先頭から順に取り出したい場合は、List<T>ではなくQueue<T>の使用も検討できます。

C#
Queue<string> queue = new Queue<string>();

queue.Enqueue("A");
queue.Enqueue("B");
queue.Enqueue("C");

string item = queue.Dequeue();

Console.WriteLine(item);

先頭から頻繁に削除する用途では、データ構造そのものを見直すことも大切です。

8-3. 大量削除ではRemoveAllや別List作成も検討する

条件に一致する要素を大量に削除する場合は、RemoveAtを繰り返すよりRemoveAllの方が簡潔で効率的なことがあります。

C#
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

numbers.RemoveAll(n => n % 2 == 0);

条件に一致する要素をすべて削除したいなら、このように書けます。

また、削除するのではなく、必要な要素だけを新しいListに作る方法もあります。

C#
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

List<int> filtered = numbers
.Where(n => n % 2 != 0)
.ToList();

この方法では、元のListを直接変更せず、条件に合う要素だけを新しいListとして取得できます。

削除対象が少ない場合や、特定の位置だけ削除したい場合はRemoveAtが便利です。一方、大量データの条件削除では、RemoveAllや新しいListの作成も選択肢になります。

8-4. 配列ではなくListを使うメリット

C#には配列もありますが、配列は基本的にサイズが固定です。

C#
int[] numbers = { 1, 2, 3 };

配列から要素を削除してサイズを小さくするには、新しい配列を作り直す必要があります。

一方、List<T>は要素の追加や削除を扱いやすいコレクションです。

C#
List<int> numbers = new List<int> { 1, 2, 3 };

numbers.RemoveAt(1);
numbers.Add(4);

このように、Listは要素数が変わるデータを扱うのに向いています。

ただし、Listでも途中の要素を削除すると後続要素の移動が発生します。要素の追加削除が多い場合は、削除位置や処理回数を考えて使うことが大切です。

9. C# List.RemoveAtに関するよくある質問

9-1. RemoveAtで削除した要素は取得できる?

RemoveAtは削除した要素を返しません。戻り値はvoidです。

そのため、削除した要素を使いたい場合は、削除前に取得しておく必要があります。

C#
List<string> names = new List<string>
{
"Alice",
"Bob",
"Charlie"
};

int index = 1;

if (index >= 0 && index < names.Count)
{
string removedItem = names[index];
names.RemoveAt(index);

Console.WriteLine($"削除した要素: {removedItem}");
}

実行結果は次のとおりです。

削除した要素: Bob

RemoveAtを実行した後は、その位置の要素は削除されているため、削除前に変数へ保存しておきましょう。

9-2. 存在しないインデックスを指定するとどうなる?

存在しないインデックスを指定すると、ArgumentOutOfRangeExceptionが発生します。

C#
List<int> numbers = new List<int> { 10, 20, 30 };

numbers.RemoveAt(5);

この例では、インデックス5は存在しないためエラーになります。

有効なインデックスは次の範囲です。

0 <= index < list.Count

安全に書くには、次のようにチェックします。

C#
if (index >= 0 && index < numbers.Count)
{
numbers.RemoveAt(index);
}

9-3. 最後の要素を削除するにはどう書く?

最後の要素を削除するには、Count - 1を指定します。

C#
List<string> items = new List<string>
{
"A",
"B",
"C"
};

if (items.Count > 0)
{
items.RemoveAt(items.Count - 1);
}

Console.WriteLine(string.Join(", ", items));

実行結果は次のとおりです。

A, B

Listが空の場合、items.Count - 1-1になります。そのため、必ずitems.Count > 0を確認してから削除しましょう。

9-4. Listの要素数が1つだけでもRemoveAtは使える?

はい、使えます。

Listの要素数が1つの場合、有効なインデックスは0だけです。

C#
List<string> items = new List<string> { "Only" };

items.RemoveAt(0);

Console.WriteLine(items.Count);

実行結果は次のとおりです。

0

RemoveAt(0)によって唯一の要素が削除され、Listは空になります。

ただし、削除後にもう一度RemoveAt(0)を実行すると、Listが空なのでエラーになります。

C#
if (items.Count > 0)
{
items.RemoveAt(0);
}

要素数が1つだけの場合でも、削除前にCountを確認する習慣をつけると安全です。

9-5. RemoveAtとnullの関係は?

RemoveAtは、指定したインデックスの要素を削除するメソッドです。そのため、要素の値がnullかどうかは基本的に関係ありません。

たとえば、次のようにnullを含むListがあるとします。

C#
List<string?> names = new List<string?>
{
"Alice",
null,
"Charlie"
};

names.RemoveAt(1);

Console.WriteLine(string.Join(", ", names));

この場合、インデックス1にあるnullが削除されます。

RemoveAtは値ではなく位置で削除するため、削除対象がnullでも問題ありません。

一方で、Listそのものがnullの場合は、RemoveAtを呼び出せません。

C#
List<string>? names = null;

names.RemoveAt(0);

このコードはNullReferenceExceptionになります。

List自体がnullの可能性がある場合は、次のように確認します。

C#
if (names != null && names.Count > 0)
{
names.RemoveAt(0);
}

また、nullという値を指定して削除したい場合は、RemoveAtではなくRemoveを使うこともできます。

C#
names?.Remove(null);

RemoveAtはインデックス指定、Removeは値指定という違いを意識しましょう。

まとめ

C#のList.RemoveAtは、List内の指定したインデックス位置にある要素を削除するメソッドです。

基本構文は次のとおりです。

C#
list.RemoveAt(index);

先頭の要素を削除する場合は、次のように書きます。

C#
list.RemoveAt(0);

末尾の要素を削除する場合は、次のように書きます。

C#
list.RemoveAt(list.Count - 1);

ただし、RemoveAtでは存在しないインデックスを指定するとArgumentOutOfRangeExceptionが発生します。そのため、実務では次のように範囲チェックを入れるのが安全です。

C#
if (index >= 0 && index < list.Count)
{
list.RemoveAt(index);
}

また、条件に合う要素を削除したい場合は、IndexOfFindIndexでインデックスを取得してからRemoveAtを使います。

C#
int index = list.FindIndex(x => 条件);

if (index >= 0)
{
list.RemoveAt(index);
}

複数要素をループで削除する場合は、前から削除すると要素がずれる可能性があります。安全に処理するには、後ろからループします。

C#
for (int i = list.Count - 1; i >= 0; i--)
{
if (条件)
{
list.RemoveAt(i);
}
}

RemoveAtは「位置を指定して削除する」メソッドです。値を指定して削除したい場合はRemove、条件に一致する複数要素を削除したい場合はRemoveAll、範囲で削除したい場合はRemoveRange、すべて削除したい場合はClearを使います。

c# list removeatを正しく使うには、インデックスが0から始まること、削除後に後続要素が前に詰められること、範囲外のインデックスを指定するとエラーになることを理解しておくことが重要です。