C# List.Sortの使い方を完全解説|昇順・降順・自作クラスの並び替えまでサンプル付き
はじめに
C#でリストの中身を並び替えたいときによく使うのが、List<T>のSortメソッドです。
たとえば、数値を小さい順に並べる、文字列をアルファベット順にする、商品価格を安い順にする、ユーザー一覧を年齢順に並べる、といった処理で使われます。
C#var numbers = new List<int> { 3, 1, 5, 2, 4 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 4, 5
List.Sortはシンプルに使える一方で、自作クラスを並び替える場合や、降順・複数条件・nullを含むListを扱う場合には注意点もあります。
この記事では、c# list sortで知りたい基本から実践的な使い方まで、サンプル付きで解説します。
1. C#のList.Sortとは?できることと基本イメージ
1-1. List.SortはList<T>の要素をその場で並び替えるメソッド
List.Sortは、List<T>に入っている要素を並び替えるためのメソッドです。
重要なのは、Sortは元のList自体の順番を変更するという点です。
C#var numbers = new List<int> { 30, 10, 20 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 10, 20, 30
この例では、numbersの中身そのものが並び替わっています。
Sortは戻り値を返しません。つまり、次のようには書けません。
C#// エラーになる
var sortedNumbers = numbers.Sort();
Sortの戻り値はvoidなので、並び替えた後のListをそのまま使います。
C#numbers.Sort();
foreach (var number in numbers)
{
Console.WriteLine(number);
}
1-2. 昇順・降順・自作クラスの並び替えに使える
List.Sortでは、次のような並び替えができます。
数値を昇順に並べる例です。
C#var scores = new List<int> { 80, 100, 60, 90 };
scores.Sort();
Console.WriteLine(string.Join(", ", scores));
// 60, 80, 90, 100
文字列を並べる例です。
C#var names = new List<string> { "Taro", "Hanako", "Jiro" };
names.Sort();
Console.WriteLine(string.Join(", ", names));
// Hanako, Jiro, Taro
降順にしたい場合は、Sort後にReverseを使う方法があります。
C#scores.Sort();
scores.Reverse();
Console.WriteLine(string.Join(", ", scores));
// 100, 90, 80, 60
また、自作クラスのListも、並び替え条件を指定すればSortできます。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};
users.Sort((a, b) => a.Age.CompareTo(b.Age));
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}
// Jiro: 20
// Hanako: 25
// Taro: 30
C#class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
1-3. 配列のArray.SortやLINQのOrderByとの違い
C#には、List.Sort以外にも並び替えの方法があります。
配列を並び替える場合はArray.Sortを使います。
C#int[] numbers = { 3, 1, 2 };
Array.Sort(numbers);
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3
Array.Sortは配列用、List.SortはList<T>用です。
一方、LINQのOrderByは、元のListを変更せずに、並び替えた結果を返します。
C#var numbers = new List<int> { 3, 1, 2 };
var sortedNumbers = numbers.OrderBy(x => x).ToList();
Console.WriteLine(string.Join(", ", numbers));
// 3, 1, 2
Console.WriteLine(string.Join(", ", sortedNumbers));
// 1, 2, 3
List.Sortは元のListを変更しますが、OrderByは元のListを変更しません。
使い分けの目安は次のとおりです。
C#// 元のListを直接並び替えたい
numbers.Sort();
// 元のListはそのままで、並び替えた結果がほしい
var sorted = numbers.OrderBy(x => x).ToList();
2. List.Sortの基本的な使い方
2-1. 数値リストを昇順に並び替えるサンプル
数値のListを昇順、つまり小さい順に並べる場合は、Sort()を呼び出すだけです。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 5, 8
intやdoubleなどの数値型は、標準で比較ルールを持っているため、条件を指定しなくても並び替えられます。
C#var prices = new List<decimal> { 1200m, 500m, 980m };
prices.Sort();
Console.WriteLine(string.Join(", ", prices));
// 500, 980, 1200
2-2. 文字列リストをアルファベット順に並び替えるサンプル
文字列のListも、Sort()だけで並び替えできます。
C#var fruits = new List<string> { "banana", "apple", "orange", "grape" };
fruits.Sort();
Console.WriteLine(string.Join(", ", fruits));
// apple, banana, grape, orange
大文字・小文字が混在している場合は、期待した順番と異なることがあります。
C#var words = new List<string> { "apple", "Banana", "orange", "Grape" };
words.Sort();
Console.WriteLine(string.Join(", ", words));
大文字・小文字を無視して並び替えたい場合は、比較ルールを指定します。
C#words.Sort(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(string.Join(", ", words));
日本語文字列を扱う場合も、標準の比較順がそのまま使われます。
C#var names = new List<string> { "佐藤", "田中", "鈴木" };
names.Sort();
Console.WriteLine(string.Join(", ", names));
文字列の並び順を厳密に制御したい場合は、StringComparerやカルチャを意識した比較を使うことがあります。
2-3. Sort後は元のList自体の順番が変わる点に注意
List.Sortで特に注意したいのは、元のList自体が変更されることです。
C#var numbers = new List<int> { 3, 1, 2 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3
並び替え前の順番を後で使いたい場合は、コピーを作ってからSortします。
C#var original = new List<int> { 3, 1, 2 };
var sorted = new List<int>(original);
sorted.Sort();
Console.WriteLine("original: " + string.Join(", ", original));
// original: 3, 1, 2
Console.WriteLine("sorted: " + string.Join(", ", sorted));
// sorted: 1, 2, 3
または、LINQのOrderByを使う方法もあります。
C#var original = new List<int> { 3, 1, 2 };
var sorted = original.OrderBy(x => x).ToList();
元の順番を残したいならOrderBy、元のListを直接並び替えてよいならSort、と考えると分かりやすいです。
2-4. nullや空のListをSortするとどうなるか
空のListに対してSortを呼び出してもエラーにはなりません。
C#var numbers = new List<int>();
numbers.Sort();
Console.WriteLine(numbers.Count);
// 0
要素が1つだけのListでも問題ありません。
C#var numbers = new List<int> { 10 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 10
ただし、List変数自体がnullの場合はエラーになります。
C#List<int>? numbers = null;
// NullReferenceException
numbers.Sort();
この場合は、Sortの前にnullチェックをします。
C#if (numbers != null)
{
numbers.Sort();
}
また、参照型のListにnull要素が含まれている場合も注意が必要です。
C#var names = new List<string?> { "Taro", null, "Hanako" };
names.Sort();
foreach (var name in names)
{
Console.WriteLine(name ?? "(null)");
}
標準の比較ではnullを扱える場合がありますが、自分で比較処理を書くときにx.Lengthのように直接プロパティへアクセスすると、nullで例外が発生します。
C#var names = new List<string?> { "Taro", null, "Hanako" };
// nullがあると危険
names.Sort((a, b) => a!.Length.CompareTo(b!.Length));
nullを含む可能性がある場合は、明示的に処理しましょう。
C#names.Sort((a, b) =>
{
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return a.Length.CompareTo(b.Length);
});
3. List.Sortで降順に並び替える方法
3-1. Sort後にReverseを使って降順にする方法
もっとも分かりやすい降順ソートは、Sortした後にReverseする方法です。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
numbers.Sort();
numbers.Reverse();
Console.WriteLine(string.Join(", ", numbers));
// 8, 5, 3, 2, 1
Sortで昇順にしてから、Reverseで逆順にしています。
文字列でも同じように使えます。
C#var fruits = new List<string> { "banana", "apple", "orange" };
fruits.Sort();
fruits.Reverse();
Console.WriteLine(string.Join(", ", fruits));
// orange, banana, apple
この方法は読みやすく、初心者にも分かりやすい書き方です。
ただし、並び替え条件が複雑になってくる場合は、最初から比較処理で降順を指定した方が自然です。
3-2. 比較処理を指定して最初から降順にする方法
Sortには、比較処理を指定する書き方があります。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
numbers.Sort((a, b) => b.CompareTo(a));
Console.WriteLine(string.Join(", ", numbers));
// 8, 5, 3, 2, 1
昇順の場合は、次のように書きます。
C#numbers.Sort((a, b) => a.CompareTo(b));
降順の場合は、比較する順番を逆にします。
C#numbers.Sort((a, b) => b.CompareTo(a));
つまり、a.CompareTo(b)なら昇順、b.CompareTo(a)なら降順です。
自作クラスのプロパティでも同じ考え方です。
C#users.Sort((a, b) => b.Age.CompareTo(a.Age));
これは、年齢が大きい順に並び替える処理です。
3-3. Comparer<T>.Createを使った降順ソート
Comparer<T>.Createを使って、比較ルールを作ることもできます。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
var descendingComparer = Comparer<int>.Create((a, b) => b.CompareTo(a));
numbers.Sort(descendingComparer);
Console.WriteLine(string.Join(", ", numbers));
// 8, 5, 3, 2, 1
この書き方は、比較ルールに名前を付けたい場合や、複数箇所で同じ比較ルールを使いたい場合に便利です。
自作クラスでも使えます。
C#var ageDescendingComparer = Comparer<User>.Create(
(a, b) => b.Age.CompareTo(a.Age)
);
users.Sort(ageDescendingComparer);
比較処理が短い場合はラムダ式を直接書く、再利用したい場合はComparer<T>.Createを使う、と考えるとよいでしょう。
3-4. 降順ソートでよくある書き方の違い
降順ソートには、いくつかの書き方があります。
C#numbers.Sort();
numbers.Reverse();
これは、昇順に並び替えてから逆順にする書き方です。シンプルで分かりやすいです。
C#numbers.Sort((a, b) => b.CompareTo(a));
これは、比較処理で直接降順を指定する書き方です。自作クラスやプロパティ指定のソートでよく使います。
C#var sorted = numbers.OrderByDescending(x => x).ToList();
これはLINQを使う方法です。元のListを変更せずに、降順の新しいListを作ります。
それぞれの違いを整理すると、次のようになります。
C#// 元のListを変更してよい。簡単に降順にしたい。
numbers.Sort();
numbers.Reverse();
// 元のListを変更してよい。条件を指定して降順にしたい。
numbers.Sort((a, b) => b.CompareTo(a));
// 元のListを残したい。
var sorted = numbers.OrderByDescending(x => x).ToList();
4. ラムダ式を使ってList.Sortの並び替え条件を指定する
4-1. Comparison<T>を使ったSortの書き方
List.Sortでは、ラムダ式で比較処理を指定できます。
C#list.Sort((a, b) => a.CompareTo(b));
このラムダ式は、Comparison<T>というデリゲートに対応しています。
Comparison<T>は、2つの値を比較して、intを返す処理です。
C#int Compare(T a, T b)
戻り値の意味は次のとおりです。
C#// aをbより前に並べたい場合
return -1;
// aとbを同じ順序として扱う場合
return 0;
// aをbより後ろに並べたい場合
return 1;
実際には、CompareToを使って書くことが多いです。
C#numbers.Sort((a, b) => a.CompareTo(b));
4-2. 数値を昇順・降順にするラムダ式サンプル
数値を昇順にする場合は、次のように書きます。
C#var numbers = new List<int> { 3, 1, 5, 2, 4 };
numbers.Sort((a, b) => a.CompareTo(b));
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 4, 5
降順にする場合は、aとbを逆にします。
C#var numbers = new List<int> { 3, 1, 5, 2, 4 };
numbers.Sort((a, b) => b.CompareTo(a));
Console.WriteLine(string.Join(", ", numbers));
// 5, 4, 3, 2, 1
次のように引き算で書く例も見かけます。
C#numbers.Sort((a, b) => a - b);
ただし、値が大きい場合にオーバーフローする可能性があるため、基本的にはCompareToを使う方が安全です。
C#numbers.Sort((a, b) => a.CompareTo(b));
4-3. 文字列の長さで並び替えるサンプル
文字列をアルファベット順ではなく、文字数の短い順に並べたい場合は、Lengthを比較します。
C#var words = new List<string> { "apple", "kiwi", "banana", "fig" };
words.Sort((a, b) => a.Length.CompareTo(b.Length));
Console.WriteLine(string.Join(", ", words));
// fig, kiwi, apple, banana
長い順にしたい場合は、比較を逆にします。
C#words.Sort((a, b) => b.Length.CompareTo(a.Length));
Console.WriteLine(string.Join(", ", words));
// banana, apple, kiwi, fig
文字数が同じ場合にアルファベット順にしたい場合は、複数条件で比較します。
C#words.Sort((a, b) =>
{
int lengthResult = a.Length.CompareTo(b.Length);
if (lengthResult != 0)
{
return lengthResult;
}
return a.CompareTo(b);
});
この例では、まず文字数で比較し、文字数が同じ場合だけ文字列そのものを比較しています。
4-4. 複数条件で並び替えたい場合の考え方
複数条件で並び替える場合は、優先したい条件から順番に比較します。
たとえば、ユーザー一覧を「年齢の昇順、年齢が同じなら名前順」にしたい場合は、次のように書きます。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 30 },
new User { Name = "Akira", Age = 25 }
};
users.Sort((a, b) =>
{
int ageResult = a.Age.CompareTo(b.Age);
if (ageResult != 0)
{
return ageResult;
}
return a.Name.CompareTo(b.Name);
});
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}
出力例です。
C#Akira: 25
Hanako: 25
Jiro: 30
Taro: 30
比較処理では、最初の条件で差があればその結果を返します。差がなければ次の条件を比較します。
LINQを使う場合は、OrderByとThenByでより読みやすく書けます。
C#var sortedUsers = users
.OrderBy(user => user.Age)
.ThenBy(user => user.Name)
.ToList();
元のListを直接変更したいならSort、複数条件を読みやすく書きたいならOrderByとThenByが便利です。
5. 自作クラスのListをSortで並び替える方法
5-1. 自作クラスをそのままSortするとエラーになる理由
自作クラスのListに対して、何も指定せずにSort()を呼び出すとエラーになることがあります。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 }
};
// エラーになる可能性がある
users.Sort();
intやstringは標準で比較方法を持っています。
しかし、自作クラスの場合、C#は「何を基準に並び替えればよいか」を判断できません。
たとえば、次のUserクラスがあるとします。
C#class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
このクラスにはNameとAgeがあります。
名前順にしたいのか、年齢順にしたいのか、C#には分かりません。そのため、比較ルールを指定する必要があります。
5-2. プロパティを指定して並び替えるサンプル
自作クラスのListを並び替えるには、ラムダ式で比較するプロパティを指定します。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};
users.Sort((a, b) => a.Age.CompareTo(b.Age));
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}
出力結果です。
C#Jiro: 20
Hanako: 25
Taro: 30
a.Age.CompareTo(b.Age)と書くことで、Ageの昇順に並び替えています。
名前順にしたい場合は、Nameを比較します。
C#users.Sort((a, b) => a.Name.CompareTo(b.Name));
このように、自作クラスでは「どのプロパティで比較するか」を明示するのが基本です。
5-3. 年齢・点数・日付など数値系プロパティで並び替える
年齢順に並び替える例です。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
年齢の降順にする場合は、次のようにします。
C#users.Sort((a, b) => b.Age.CompareTo(a.Age));
点数順に並び替える例です。
C#class Student
{
public string Name { get; set; } = "";
public int Score { get; set; }
}
C#var students = new List<Student>
{
new Student { Name = "Taro", Score = 80 },
new Student { Name = "Hanako", Score = 95 },
new Student { Name = "Jiro", Score = 70 }
};
students.Sort((a, b) => a.Score.CompareTo(b.Score));
foreach (var student in students)
{
Console.WriteLine($"{student.Name}: {student.Score}");
}
点数が高い順にしたい場合は、降順にします。
C#students.Sort((a, b) => b.Score.CompareTo(a.Score));
日付で並び替える場合も同じです。
C#class Article
{
public string Title { get; set; } = "";
public DateTime PublishedAt { get; set; }
}
C#var articles = new List<Article>
{
new Article { Title = "記事A", PublishedAt = new DateTime(2024, 5, 1) },
new Article { Title = "記事B", PublishedAt = new DateTime(2024, 3, 10) },
new Article { Title = "記事C", PublishedAt = new DateTime(2024, 6, 20) }
};
articles.Sort((a, b) => a.PublishedAt.CompareTo(b.PublishedAt));
新しい順にしたい場合は、次のようにします。
C#articles.Sort((a, b) => b.PublishedAt.CompareTo(a.PublishedAt));
5-4. 名前など文字列プロパティで並び替える
名前順に並び替える場合は、文字列プロパティを比較します。
C#users.Sort((a, b) => a.Name.CompareTo(b.Name));
サンプル全体は次のとおりです。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};
users.Sort((a, b) => a.Name.CompareTo(b.Name));
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}
出力例です。
C#Hanako: 25
Jiro: 20
Taro: 30
大文字・小文字を無視して並び替えたい場合は、StringComparerを使います。
C#users.Sort((a, b) =>
StringComparer.OrdinalIgnoreCase.Compare(a.Name, b.Name)
);
Nameがnullになる可能性がある場合は、null対策も入れておくと安全です。
C#users.Sort((a, b) =>
string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)
);
5-5. 昇順・降順を切り替える書き方
昇順と降順は、CompareToの左右を入れ替えることで切り替えられます。
年齢の昇順です。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
年齢の降順です。
C#users.Sort((a, b) => b.Age.CompareTo(a.Age));
名前の昇順です。
C#users.Sort((a, b) => a.Name.CompareTo(b.Name));
名前の降順です。
C#users.Sort((a, b) => b.Name.CompareTo(a.Name));
フラグで昇順・降順を切り替えたい場合は、次のように書けます。
C#bool ascending = false;
users.Sort((a, b) =>
{
int result = a.Age.CompareTo(b.Age);
return ascending ? result : -result;
});
ただし、単純な比較であれば、次のように分けて書いた方が読みやすい場合もあります。
C#if (ascending)
{
users.Sort((a, b) => a.Age.CompareTo(b.Age));
}
else
{
users.Sort((a, b) => b.Age.CompareTo(a.Age));
}
6. IComparableとIComparerを使った本格的な並び替え
6-1. IComparable<T>でクラス自身に並び替えルールを持たせる
IComparable<T>を実装すると、自作クラス自身に標準の並び替えルールを持たせることができます。
C#class User : IComparable<User>
{
public string Name { get; set; } = "";
public int Age { get; set; }
public int CompareTo(User? other)
{
if (other == null)
{
return 1;
}
return Age.CompareTo(other.Age);
}
}
このようにしておくと、users.Sort()だけで年齢順に並び替えられます。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};
users.Sort();
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}
出力結果です。
C#Jiro: 20
Hanako: 25
Taro: 30
IComparable<T>は、そのクラスの自然な並び順を定義したい場合に向いています。
たとえば、Userは年齢順で並ぶのが基本、Productは価格順で並ぶのが基本、というように決めたい場合です。
6-2. IComparer<T>で外部に並び替えルールを分離する
IComparer<T>を使うと、並び替えルールをクラスの外に分離できます。
C#class UserAgeComparer : IComparer<User>
{
public int Compare(User? x, User? y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
return x.Age.CompareTo(y.Age);
}
}
使うときは、Sortに比較クラスのインスタンスを渡します。
C#users.Sort(new UserAgeComparer());
名前順のComparerも作れます。
C#class UserNameComparer : IComparer<User>
{
public int Compare(User? x, User? y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
}
C#users.Sort(new UserNameComparer());
IComparer<T>を使うと、同じクラスに対して複数の並び替えルールを用意できます。
6-3. IComparableとIComparerの使い分け
IComparable<T>は、クラス自身に基本の並び順を持たせる方法です。
C#class Product : IComparable<Product>
{
public string Name { get; set; } = "";
public int Price { get; set; }
public int CompareTo(Product? other)
{
if (other == null) return 1;
return Price.CompareTo(other.Price);
}
}
この場合、Productは価格順で並ぶのが標準ルールになります。
C#products.Sort();
一方、IComparer<T>は、外部に並び替えルールを作る方法です。
C#class ProductNameComparer : IComparer<Product>
{
public int Compare(Product? x, Product? y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
return x.Name.CompareTo(y.Name);
}
}
C#products.Sort(new ProductNameComparer());
使い分けの目安は次のとおりです。
C#// クラスの標準的な並び順を決めたい
IComparable<T>
// 場面ごとに並び替えルールを変えたい
IComparer<T>
実務では、単発の並び替えならラムダ式、再利用したいならIComparer<T>、クラスの基本ルールとして定義したいならIComparable<T>を使うと分かりやすいです。
6-4. Compareメソッドの戻り値の意味
CompareやCompareToでは、戻り値の意味を理解しておくことが重要です。
C#public int Compare(User? x, User? y)
{
return x.Age.CompareTo(y.Age);
}
戻り値が負の数なら、xはyより前に並びます。
戻り値が0なら、xとyは同じ順序として扱われます。
戻り値が正の数なら、xはyより後ろに並びます。
たとえば、年齢の昇順は次のように書きます。
C#return x.Age.CompareTo(y.Age);
年齢の降順は次のように書きます。
C#return y.Age.CompareTo(x.Age);
または、結果を反転させることもできます。
C#return -x.Age.CompareTo(y.Age);
ただし、可読性を考えると、次のように書く方が分かりやすいことが多いです。
C#return y.Age.CompareTo(x.Age);
7. List.SortとLINQのOrderBy・OrderByDescendingの違い
7-1. List.Sortは元のListを変更する
List.Sortは、元のListを直接変更します。
C#var numbers = new List<int> { 3, 1, 2 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3
並び替え後は、元の順番は残りません。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 }
};
users.Sort((a, b) => a.Age.CompareTo(b.Age));
このコードを実行すると、users自体の順番が年齢順に変わります。
元のListをそのまま使い続けたい場合は、Sortする前にコピーを作る必要があります。
C#var sortedUsers = new List<User>(users);
sortedUsers.Sort((a, b) => a.Age.CompareTo(b.Age));
7-2. OrderByは新しい並び順の結果を返す
LINQのOrderByは、元のListを変更せず、並び替えた結果を返します。
C#var numbers = new List<int> { 3, 1, 2 };
var sortedNumbers = numbers.OrderBy(x => x).ToList();
Console.WriteLine(string.Join(", ", numbers));
// 3, 1, 2
Console.WriteLine(string.Join(", ", sortedNumbers));
// 1, 2, 3
降順にする場合は、OrderByDescendingを使います。
C#var sortedNumbers = numbers.OrderByDescending(x => x).ToList();
Console.WriteLine(string.Join(", ", sortedNumbers));
// 3, 2, 1
自作クラスでも、プロパティを指定して簡単に並び替えられます。
C#var sortedUsers = users
.OrderBy(user => user.Age)
.ToList();
降順なら次のようにします。
C#var sortedUsers = users
.OrderByDescending(user => user.Age)
.ToList();
7-3. 単純な並び替えならどちらを使うべきか
元のListを変更してよいなら、List.Sortで問題ありません。
C#numbers.Sort();
元のListを変更したくないなら、OrderByを使います。
C#var sortedNumbers = numbers.OrderBy(x => x).ToList();
単純な数値や文字列の並び替えなら、どちらでも実現できます。
ただし、コードの意図は少し違います。
C#// このListそのものを並び替える
numbers.Sort();
// 並び替えた別の結果を作る
var sorted = numbers.OrderBy(x => x).ToList();
実務では、画面表示用に一時的に並び替えたい場合はOrderBy、内部データの順番そのものを変えたい場合はSortが使いやすいです。
7-4. ThenByを使った複数条件ソートとの比較
複数条件の並び替えは、LINQのThenByを使うと読みやすく書けます。
C#var sortedUsers = users
.OrderBy(user => user.Age)
.ThenBy(user => user.Name)
.ToList();
これは、「年齢の昇順、年齢が同じなら名前順」という意味です。
降順を組み合わせることもできます。
C#var sortedUsers = users
.OrderByDescending(user => user.Age)
.ThenBy(user => user.Name)
.ToList();
同じことをList.Sortで書くと、次のようになります。
C#users.Sort((a, b) =>
{
int ageResult = b.Age.CompareTo(a.Age);
if (ageResult != 0)
{
return ageResult;
}
return a.Name.CompareTo(b.Name);
});
List.Sortでも複数条件ソートは可能ですが、条件が増えるとコードが長くなります。
読みやすさを重視するなら、複数条件ではOrderBy、ThenByを使うのも有力です。
8. List.Sortでよくあるエラーと注意点
8-1. Failed to compare two elements in the arrayの原因
List.Sortを使っていると、次のようなエラーが出ることがあります。
C#Failed to compare two elements in the array.
これは、並び替え中に要素同士を比較できなかった場合に発生します。
よくある原因は次のようなものです。
C#// 自作クラスに比較ルールがない
users.Sort();
// 比較処理の中で例外が発生している
users.Sort((a, b) => a.Name.Length.CompareTo(b.Name.Length));
// nullを考慮していない
users.Sort((a, b) => a.Name.CompareTo(b.Name));
たとえば、Nameがnullの可能性があるのに、直接CompareToを呼び出すと例外が発生します。
C#users.Sort((a, b) => a.Name.CompareTo(b.Name));
安全に書くなら、string.Compareを使う方法があります。
C#users.Sort((a, b) =>
string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)
);
8-2. 自作クラスで比較ルールを指定していない場合
自作クラスをそのままSort()しようとすると、比較方法が分からずエラーになることがあります。
C#var products = new List<Product>
{
new Product { Name = "PC", Price = 120000 },
new Product { Name = "Mouse", Price = 3000 }
};
// 比較ルールがないためエラーになる可能性がある
products.Sort();
この場合は、プロパティを指定して並び替えます。
C#products.Sort((a, b) => a.Price.CompareTo(b.Price));
または、IComparable<T>を実装します。
C#class Product : IComparable<Product>
{
public string Name { get; set; } = "";
public int Price { get; set; }
public int CompareTo(Product? other)
{
if (other == null) return 1;
return Price.CompareTo(other.Price);
}
}
これで、次のように書けるようになります。
C#products.Sort();
8-3. nullを含むListを並び替えるときの注意
Listの中にnullが含まれている場合、比較処理でエラーになることがあります。
C#var users = new List<User?>
{
new User { Name = "Taro", Age = 30 },
null,
new User { Name = "Hanako", Age = 25 }
};
この状態で、次のように書くと危険です。
C#users.Sort((a, b) => a!.Age.CompareTo(b!.Age));
aまたはbがnullの場合、例外が発生します。
nullを含む可能性がある場合は、明示的に処理します。
C#users.Sort((a, b) =>
{
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return a.Age.CompareTo(b.Age);
});
この例では、nullを先頭に並べています。
nullを最後にしたい場合は、戻り値を逆にします。
C#users.Sort((a, b) =>
{
if (a == null && b == null) return 0;
if (a == null) return 1;
if (b == null) return -1;
return a.Age.CompareTo(b.Age);
});
8-4. CompareToで戻り値を逆にしてしまうミス
CompareToを使うときによくあるミスが、昇順と降順を逆に書いてしまうことです。
昇順は次の書き方です。
C#numbers.Sort((a, b) => a.CompareTo(b));
降順は次の書き方です。
C#numbers.Sort((a, b) => b.CompareTo(a));
自作クラスでも同じです。
C#// 年齢の昇順
users.Sort((a, b) => a.Age.CompareTo(b.Age));
// 年齢の降順
users.Sort((a, b) => b.Age.CompareTo(a.Age));
「小さい順にしたいのに大きい順になった」「ランキングの順位が逆になった」という場合は、CompareToの左右が逆になっていないか確認しましょう。
8-5. Sortは安定ソートではない点に注意
List.Sortは安定ソートではありません。
安定ソートとは、比較結果が同じ要素同士の元の順番が維持されるソートのことです。
たとえば、年齢だけで並び替える場合を考えます。
C#var users = new List<User>
{
new User { Name = "A", Age = 20 },
new User { Name = "B", Age = 20 },
new User { Name = "C", Age = 30 }
};
users.Sort((a, b) => a.Age.CompareTo(b.Age));
AとBはどちらもAgeが20です。
安定ソートなら、元の順番であるA、Bが維持されます。しかし、List.Sortでは同じキーの要素の順序が保証されません。
同じ値のときにも順番を決めたい場合は、複数条件を指定します。
C#users.Sort((a, b) =>
{
int ageResult = a.Age.CompareTo(b.Age);
if (ageResult != 0)
{
return ageResult;
}
return a.Name.CompareTo(b.Name);
});
元の順番を維持した安定ソートが必要な場合は、LINQのOrderByを使うことも検討しましょう。
C#var sortedUsers = users
.OrderBy(user => user.Age)
.ToList();
9. 実践サンプルで学ぶList.Sortの使い方
9-1. 商品価格を安い順・高い順に並び替える
商品クラスを用意します。
C#class Product
{
public string Name { get; set; } = "";
public int Price { get; set; }
}
商品リストを作成します。
C#var products = new List<Product>
{
new Product { Name = "ノートPC", Price = 120000 },
new Product { Name = "マウス", Price = 3000 },
new Product { Name = "キーボード", Price = 8000 },
new Product { Name = "モニター", Price = 25000 }
};
安い順に並び替える場合です。
C#products.Sort((a, b) => a.Price.CompareTo(b.Price));
foreach (var product in products)
{
Console.WriteLine($"{product.Name}: {product.Price}円");
}
出力例です。
C#マウス: 3000円
キーボード: 8000円
モニター: 25000円
ノートPC: 120000円
高い順に並び替える場合です。
C#products.Sort((a, b) => b.Price.CompareTo(a.Price));
出力例です。
C#ノートPC: 120000円
モニター: 25000円
キーボード: 8000円
マウス: 3000円
価格順の並び替えは、ECサイトの商品一覧や管理画面などでよく使われます。
9-2. ユーザー一覧を年齢順に並び替える
ユーザー一覧を年齢順に並び替える例です。
C#class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
C#var users = new List<User>
{
new User { Name = "Taro", Age = 32 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 28 }
};
年齢の若い順に並び替えます。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}歳");
}
出力例です。
C#Hanako: 25歳
Jiro: 28歳
Taro: 32歳
年齢の高い順にする場合です。
C#users.Sort((a, b) => b.Age.CompareTo(a.Age));
年齢が同じ場合に名前順にしたいなら、複数条件を指定します。
C#users.Sort((a, b) =>
{
int ageResult = a.Age.CompareTo(b.Age);
if (ageResult != 0)
{
return ageResult;
}
return a.Name.CompareTo(b.Name);
});
9-3. 日付の新しい順・古い順に並び替える
記事やニュース、ログなどでは、日付順の並び替えをよく使います。
C#class News
{
public string Title { get; set; } = "";
public DateTime PublishedAt { get; set; }
}
C#var newsList = new List<News>
{
new News { Title = "ニュースA", PublishedAt = new DateTime(2024, 4, 10) },
new News { Title = "ニュースB", PublishedAt = new DateTime(2024, 6, 1) },
new News { Title = "ニュースC", PublishedAt = new DateTime(2024, 5, 20) }
};
古い順に並び替える場合です。
C#newsList.Sort((a, b) => a.PublishedAt.CompareTo(b.PublishedAt));
foreach (var news in newsList)
{
Console.WriteLine($"{news.PublishedAt:yyyy/MM/dd} {news.Title}");
}
出力例です。
C#2024/04/10 ニュースA
2024/05/20 ニュースC
2024/06/01 ニュースB
新しい順に並び替える場合です。
C#newsList.Sort((a, b) => b.PublishedAt.CompareTo(a.PublishedAt));
出力例です。
C#2024/06/01 ニュースB
2024/05/20 ニュースC
2024/04/10 ニュースA
新着順の一覧を作る場合は、日付の降順を使うことが多いです。
9-4. スコアが高い順にランキング表示する
最後に、ランキング表示の例です。
C#class Player
{
public string Name { get; set; } = "";
public int Score { get; set; }
}
C#var players = new List<Player>
{
new Player { Name = "Taro", Score = 1200 },
new Player { Name = "Hanako", Score = 1800 },
new Player { Name = "Jiro", Score = 1500 },
new Player { Name = "Akira", Score = 1800 }
};
スコアが高い順に並び替えます。
C#players.Sort((a, b) => b.Score.CompareTo(a.Score));
int rank = 1;
foreach (var player in players)
{
Console.WriteLine($"{rank}位 {player.Name}: {player.Score}点");
rank++;
}
出力例です。
C#1位 Hanako: 1800点
2位 Akira: 1800点
3位 Jiro: 1500点
4位 Taro: 1200点
スコアが同じ場合に名前順にしたい場合は、複数条件を指定します。
C#players.Sort((a, b) =>
{
int scoreResult = b.Score.CompareTo(a.Score);
if (scoreResult != 0)
{
return scoreResult;
}
return a.Name.CompareTo(b.Name);
});
このようにすると、「スコアが高い順、同点なら名前順」というランキングを作れます。
C#1位 Akira: 1800点
2位 Hanako: 1800点
3位 Jiro: 1500点
4位 Taro: 1200点
まとめ
C#のList.Sortは、List<T>の要素を並び替えるための基本的なメソッドです。
数値や文字列のように標準で比較できる型であれば、Sort()を呼び出すだけで昇順に並び替えられます。
C#numbers.Sort();
降順にしたい場合は、Sort後にReverseする方法があります。
C#numbers.Sort();
numbers.Reverse();
また、ラムダ式を使えば、最初から降順や特定のプロパティ順に並び替えできます。
C#numbers.Sort((a, b) => b.CompareTo(a));
自作クラスのListを並び替える場合は、どのプロパティで比較するのかを指定する必要があります。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
本格的に並び替えルールを管理したい場合は、IComparable<T>やIComparer<T>を使う方法もあります。
C#products.Sort(new ProductPriceComparer());
List.SortとLINQのOrderByの違いも重要です。
List.Sortは元のListを変更します。
C#numbers.Sort();
OrderByは元のListを変更せず、並び替えた結果を返します。
C#var sortedNumbers = numbers.OrderBy(x => x).ToList();
元のListを直接並び替えたい場合はList.Sort、元のListを残したい場合はOrderByを使うとよいでしょう。
List.Sortを使いこなせるようになると、数値、文字列、日付、自作クラス、ランキング、商品一覧など、さまざまなデータを目的に合わせて並び替えられるようになります。

