Сортировка элементов списка List

В предыдущей части мы познакомились с тем, что из себя представляет универсальный список 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);
            }
        }
    }
}

то гарантированно получим исключение следующего содержания:

ArgumentException: At least one object must implement 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.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии