Содержание
В предыдущей части мы познакомились с тем, что из себя представляет универсальный список List<T>
, научились создавать такие списки и выводить значения различных элементов списка в консоль. Сегодня разберемся с тем, как сортировать списки List<T>
, содержащие различные типы элементов.
Как мы уже знаем, в классе List<T>
предусмотрен метод Sort()
, который производит сортировку элементов списка с использованием компаратора по умолчанию. Что это означает рассмотрим на примере.
Пример использования метода Sort() для простых типов данных
Создадим список строк и попробуем его отсортировать:
List<string> list = new List<string>(); list.Add("Вася"); list.Add("Петя"); list.Add("Ваня"); list.Add("Гоша"); list.Add("Таня"); Console.WriteLine("Несортированный список"); foreach (string str in list) Console.WriteLine(str); Console.WriteLine("Отсортированный список"); list.Sort(); foreach (string str in list) Console.WriteLine(str);
Результат выполнения программы будет следующим:
Петя
Ваня
Гоша
Таня
Cортированный список
Ваня
Вася
Гоша
Петя
Таня
То есть мы получили ровно то, что и ожидали — все строки с именами отсортировались по алфавиту. Аналогичным образом, метод Sort() сработает и на числах. Однако, если мы попробуем выполнить вот такой код:
using System; using System.Collections.Generic; namespace ListExample3 { class Person { public string Name { get; set; } } class Program { static void Main(string[] args) { List<Person> people = new List<Person>(2); people.Add(new Person() { Name = "Вася" }); people.Add(new Person() { Name = "Петя" }); people.Add(new Person() { Name = "Ваня" }); people.Add(new Person() { Name = "Гоша" }); people.Add(new Person() { Name = "Таня" }); Console.WriteLine("Несортированный список"); foreach (Person person in people) { Console.WriteLine(person.Name); } people.Sort(); Console.WriteLine("Cортированный список"); foreach (Person person in people) { Console.WriteLine(person.Name); } } } }
то гарантированно получим исключение следующего содержания:
IComparable
Для элементов списка не был обнаружен компаратор по умолчанию в результате чего мы и получили сообщение об исключении в котором говориться, что хотя бы один элемент списка должен реализовывать интерфейс IComparable
.
Пример использования метода Sort() с компаратором по умолчанию для объектов
Попробуем переписать наш предыдущий пример следующим образом:
using System; using System.Collections.Generic; namespace ListExample3 { class Person : IComparable { public string Name { get; set; } public int CompareTo(object? obj) { if ((obj == null) || (!(obj is Person))) return 0; else return string.Compare(Name, ((Person)obj).Name); } } class Program { static void Main(string[] args) { List<Person> people = new List<Person>(2); people.Add(new Person() { Name = "Вася" }); people.Add(new Person() { Name = "Петя" }); people.Add(new Person() { Name = "Ваня" }); people.Add(new Person() { Name = "Гоша" }); people.Add(new Person() { Name = "Таня" }); Console.WriteLine("Несортированный список"); foreach (Person person in people) { Console.WriteLine(person.Name); } people.Sort(); Console.WriteLine("Cортированный список"); foreach (Person person in people) { Console.WriteLine(person.Name); } } } }
Здесь наш класс Person
реализует интерфейс IComparable
и, соответственно, содержит компаратор по умолчанию — метод CompareTo
, который возвращает целочисленное значение: -1, 0 или 1. Смысл работы этого метода точно такой же, как и метода Compare
у строк. Теперь, когда мы реализовали в классе компаратор по умолчанию, метод Sort
у списка будет работать без вызова исключений, а результат будет такой же, как и при сортировке строк.
В методе CompareTo
можно реализовывать любую логику сравнения двух объектов — сравнивать объекты не по одному, а по нескольким полям, делать сортировку по возрастанию или убыванию и т.д.
Пример использования перегруженного метода Sort()
Несмотря на то, что реализация интерфейса IComparable
классом позволяет создавать любую логику сравнения элементов списка, иногда бывает необходимым производить сортировку иным образом, чем это реализовано в компараторе по умолчанию. И тогда нам на помощь может прийти перегруженный метод Sort()
, который в качестве параметра принимает метод реализующий функцию сравнения двух элементов списка. Например, перепишем нашу программы таким образом, чтобы у класса Person
появилось поле возраста человека и отсортируем список по этому полю:
using System; using System.Collections.Generic; namespace ListExample3 { class Person : IComparable { public string Name { get; set; } public byte Age { get; set; } public int CompareTo(object? obj) { if ((obj == null) || (!(obj is Person))) return 0; else return string.Compare(Name, ((Person)obj).Name); } } class Program { static void Main(string[] args) { List<Person> people = new List<Person>(2); people.Add(new Person() { Name = "Вася", Age = 10 }); people.Add(new Person() { Name = "Петя", Age = 3 }); people.Add(new Person() { Name = "Ваня", Age = 6 }); people.Add(new Person() { Name = "Гоша", Age = 18 }); people.Add(new Person() { Name = "Таня", Age = 7 }); Console.WriteLine("Несортированный список"); foreach (Person person in people) { Console.WriteLine(person.Name); } people.Sort(); Console.WriteLine("Список отсортированный по имени"); foreach (Person person in people) { Console.WriteLine($"{person.Name} возраст {person.Age}"); } people.Sort(delegate (Person x, Person y) { if (x == null && y == null) return 0; else if (x == null) return -1; else if (y == null) return 1; else return x.Age.CompareTo(y.Age); }); Console.WriteLine("Список отсортированный по возрасту"); foreach (Person person in people) { Console.WriteLine($"{person.Name} возраст {person.Age}"); } } } }
При сортировке списка по возрасту мы передали в метод Sort()
анонимный метод в котором и произвели сортировку элементов списка по необходимому нам полю, не переписывая при этом компаратор по умолчанию, реализованный в самом классе Person
. Результат работы программы будет следующий:
Вася
Петя
Ваня
Гоша
Таня
Список отсортированный по имени
Ваня возраст 6
Вася возраст 10
Гоша возраст 18
Петя возраст 3
Таня возраст 7
Список отсортированный по возрасту
Петя возраст 3
Ваня возраст 6
Таня возраст 7
Вася возраст 10
Гоша возраст 18
Итого
Сегодня мы научились производить сортировку списков, содержащих различные типы элементов. Реализовали интерфейс IComparable
в своем классе и создали компаратор по умолчанию, а также отсортировали список List<T>
, используя анонимный метод в качестве параметра метода Sort
.