Список List<T>
List<T> — класс из пространства имен System.Collections.Generic, список однотипных объектов. В отличие от массива, предоставляет набор методов, облегчающих работу, таких как добавление новых элементов (это удобно, когда неизвестно заранее сколько будет элементов).
Есть несколько вариантов создания списка:
Создание списка без начальных значений
//List<T> list = new List<T>(); //T - выбранный тип, например int
//Пример:
List<int> list = new List<int>();
//Примеры других типов:
List<string> sList = new List<string>();
List<object> oList = new List<object>();
Нет ограничений на тип, который можно поместить в список.
Создание списка с начальными значениями
//List<T> list = new List<T>(){ Item1, Item2, Item3… }; //Item1/2/3 - элементы нашего списка, тип которых T
//List<T> list = new List<T>{ Item1, Item2, Item3… }; // Тоже допустимо
//Пример:
List<int> list = new List<int> { 1, 2, 3 }; //Мы создали список с набором чисел
Создание на основе другого списка
List<int> list = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int>(list);
Надо заметить, что это не то же самое, что и list2 = list, т. к. при передаче списка в конструктор нового списка создается именно новый список, т. е. в памяти выделяется место для нового объекта, но заполняется теми же значениями, которые были в переданном списке.
Комбинированный подход
List<int> list = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int>(list) { 4 };
Если вывести все значения из списка list2, будут выведены значения от 1 до 4.
Также рассмотрим более сложный пример с пользовательским классом.
class Cup
{
public string Name { get; set; }
public int Capacity { get; set; }
public ConsoleColor Color { get; set; }
public Cup(string name)
{
Name = name;
}
}
List<Cup> cups = new List<Cup>
{
new Cup("Моя кружка") {
Color = ConsoleColor.White,
Capacity = 1000
},
new Cup("Не моя кружка") {
Color = ConsoleColor.Black,
Capacity = 300
}
};
Установка начальной емкости списка
Помимо перечисленных выше конструкторов, есть еще один, который принимает размер начальной емкости списка:
List<int> list = new List<int>(10);
Внутри списка находится массив, который при заполнении определенной емкости динамически расширяется, чтобы снизить издержки на выделение памяти при добавлении элементов. На низком уровне при проверке расширения проверяется, если в массиве (который внутри списка) нет элементов, то его размер выставляется равным _defaultCapacity (переменная в исходниках, которая имеет начальное значение = 4), в противном случае считается из «количество элементов» * 2. Т. е. всякий раз, когда мы достигаем определенной границы массива, List автоматически расширяет его в 2 раза, однако ограничение списка — это 2G (Array.MaxArrayLength – 2146435071) элементов. Для повышения производительности, если известен конечный размер списка, можно изначально задать его размер, что избавит от дополнительных выделений памяти.
Обращение к элементам списка
Аналогично как в массиве, допустимо обращаться к элементам списка в квадратных скобках по индексу.
List<int> list = new List<int>() { 3, 4, 2, 0 };
Console.WriteLine(list[0]);// 3
list[0] = 7;
Console.WriteLine(list[0]);// 7
List<int> list1 = new List<int>();
list1[0] = 1; // Получим исключение ArgumentOutOfRangeException, т.е. для начала нужно создать хоть один элемент списка
Длина списка
Для определения длины списка используется свойство Count.
List<int> list = new List<int>() { 3, 4, 2, 0 };
Console.WriteLine(list.Count); // 4
list = new List<int>();
Console.WriteLine(list.Count); // 0
Перебор списка
Для перебора списка можно использовать классический цикл, например for.
List<int> list = new List<int>() { 3, 4, 2, 0 };
for(int i = 0; i < list.Count; i++)
Console.WriteLine(list[i]);
Также существует специальный цикл для работы со списками foreach.
List<int> list = new List<int>() { 3, 4, 2, 0 };
foreach(int item in list)
Console.WriteLine(item);
Методы списка
Наконец, добрались до самого интересного — методы, которые улучшают работу со списком по сравнении с массивом. Рассмотрим следующие методы (полный список методов можно посмотреть в документации Microsoft):
Добавление в список
void Add(T item)— добавляет новый элемент с типомTв конец списка.void AddRange(IEnumerable<T> collection)— добавляет в список коллекцию или массив в конец списка.void Insert(int index, T item)— вставляет элемент в коллекцию по указанному индексу. При выходе за границы коллекции(0 > index или Count-1 < index)будет сформировано исключениеSystem.ArgumentOutOfRangeException.void InsertRange(int index, IEnumerable<T> collection)— аналогично методу выше, только вставляет список элементов по указанному индексу. Также при выходе за границы будет сформировано исключениеSystem.ArgumentOutOfRangeException.
Поиск и проверка элемента
int BinarySearch(T item)— бинарный поиск элемента в списке, возвращает индекс элемента. Работает корректно, если список отсортирован. Имеет еще два варианта реализации с дополнительными параметрами для корректного сравнения элементов или указания начальной точки поиска.bool Contains(T item)— проверка наличия элемента в списке.bool Exists(Predicate<T> match)— проверяет наличие в списке хотя бы одного элемента удовлетворяющего условиям делегатаmatch.T Find(Predicate<T> match)— возвращает первый элемент, удовлетворяющий условиям делегатаmatch.List<T> FindAll(Predicate<T> match)— аналогиченFind, только возвращает все элементы.int FindIndex(Predicate<T> match)— возвращает первый индекс элемента, удовлетворяющего условиям делегатаmatch. Имеет еще два варианта реализации с указанием позиции поиска и количества элементов.int FindLastIndex(Predicate<T> match)— аналогичноFindIndex, только возвращает индекс последнего элемента, удовлетворяющего условиям делегатаmatch. Также имеет еще два варианта реализации с указанием позиции поиска и количества элементов.List<T> GetRange(int index, int count)— этот метод получает подсписок из списка от указанного индекса с указанным количеством элементов.int IndexOf(T item)— возвращает первый индекс элемента, если он найден в списке либо-1. Также имеет еще два реализации с указанием позиции поиска и количества элементов.int LastIndexOf(T item)— возвращает последний индекс элемента, если он найден в списке либо-1. Также имеет еще два реализации с указанием позиции поиска и количества элементов.
Удаление из списка
bool Remove(T item)— удаляет первое вхождение указанного элемента из списка. Возвращаетtrue, если элемент был удален илиfalse, если нет.int RemoveAll(Predicate<T> match)— удаляет все элементы, удовлетворяющие условиям делегатаmatch. Возвращает количество удаленных элементов.void RemoveAt(int index)— удаляет элемент списка с указанным индексом. При выходе индекса за границы списка формирует исключениеSystem.ArgumentOutOfRangeException.void RemoveRange(int index, int count)— удаляет элементы массива с указанного индекса в указанном количестве. Еслиindexменьше 0 или значение параметра count меньше 0, то будет сформировано исключениеSystem.ArgumentOutOfRangeException. Если параметрыindexиcountне указывают допустимый диапазон элементов в списке, будет сформировано исключениеSystem.ArgumentException.void Clear()— удаляет все элементы из списка.
Сортировка списка
void Sort()— сортирует элементы. В основе лежит алгоритм быстрой сортировки с ограничением глубины рекурсии до 32, при достижении которого используется сортировка кучей.void Reverse()— изменяет порядок элементов во всем списке на обратный. Имеет перегруженную версию, в которой указывается начальный индекс и количество элементов.
Прочее
List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)— позволяет сконвертировать все типы элементов текущего списка в другой тип. На входе принимает делегат на метод, преобразующий объект от одного типа к другому.void CopyTo(T[] array)— копирует список в массив. Имеет еще 2 варианта реализации с указанием границ и позиций копирования.void ForEach(Action<T> action)— это аналог циклаforeach, только в качестве итерации будет выполняться метод, который на входе принимает элемент с типомT. Еще одно отличие такого цикла отforeachсостоит в том, что в методе не сработает ниbreak, ниcontinue. Аналогомcontinueпри таком подходе можно считатьreturn.T[] ToArray()— возвращает массив элементов списка.
Примеры
Добавление в список
В этом примере мы создадим массив и разными способами заполним его в цикле.
List<int> list = new List<int>();
for (int i = 0; i < 10; i++)
{
list.Add(i); // Добавление элемента в конец списка
list.AddRange(new[] { i + 4, i + 5 }); // Добавление коллекции элементов в конец списка
list.Insert(i, i + 1); // Вставка элемента по индексу i
list.InsertRange(i, new[] { i + 2, i + 3 }); // Вставка массива/коллекции по индексу i
}
Удаление из списка
List<int> list = new List<int>() { 0, 1, 2, 9, 32, -7 }; // Создали массив с элементами
Console.WriteLine(string.Join(",", list));
if (list.Remove(9)) // Удаляем 9
Console.WriteLine("Элемент 9 успешно удален.");
Console.WriteLine(string.Join(",", list));
list.RemoveAt(0); // Удаляем 0
Console.WriteLine(string.Join(",", list));
list.RemoveAll(i => i < 0); // Удаляем -7
Console.WriteLine(string.Join(",", list));
list.RemoveRange(0, 2); // Удаляес 1 и 2
Console.WriteLine(string.Join(",", list));
list.Clear(); // Удаляем все оставшиеся элементы - 32
Console.WriteLine(string.Join(",", list));
Поиск и проверка элемента
List<int> list = new List<int> { 0, 0, 1, -7, 9, 2, 32, 16, 42 };
//Индексы
//Поиск первого четного индекса
Console.WriteLine(list.FindIndex(i => i % 2 == 0)); // 0
//Поиск последнего четного индекса
Console.WriteLine(list.FindLastIndex(i => i % 2 == 0)); // 8
//Поиск индекса по числу из списка
Console.WriteLine(list.IndexOf(-7)); // 3
//Попробуем найти индекс числа которого нет в списке
Console.WriteLine(list.IndexOf(13)); // -1
//Поиск последнего индекса по числу из списка
Console.WriteLine(list.LastIndexOf(0)); // 1
list.Sort(); // Отсортируем массив, так как это основное условие бинарного поиска
Console.WriteLine(list.BinarySearch(9)); // 5
//Проверка вхождения
//Проверим есть ли в списке четные числа
Console.WriteLine(list.Exists(i => i % 2 == 0)); // true
Console.WriteLine(list.Contains(0)); // true
Console.WriteLine(list.Contains(13)); // false
//Значения
//А теперь получим первое четное число
Console.WriteLine(list.Find(i => i % 2 == 0)); // 0
//Найдем все четные числа
Console.WriteLine(string.Join(",", list.FindAll(i => i % 2 == 0))); // 0,0,2,16,32,42
//Последнее четное число
Console.WriteLine(string.Join(",", list.FindLast(i => i % 2 == 0))); // 42
//Получим подсписок, первые 4 элемента
Console.WriteLine(string.Join(",", list.GetRange(0, 4))); // -7,0,0,1
Получение диапазона и копирование в массив
List<int> list = new List<int> { 0, 0, 1, -7, 9, 2, 32, 16, 42 };
var range = list.GetRange(2, 3); // Получим диапазон/подсписок
int[] part = new int[range.Count]; // Объявим массив куда будем копировать диапазон
range.CopyTo(part); // Скопируем диапазон
Console.WriteLine(string.Join(",", part)); // 1,-7,9
Расположение элементов в обратном порядке
List<int> list = new List<int> { 0, 0, 1, -7, 9, 2, 32, 16, 42 };
list.Reverse();
Console.WriteLine(string.Join(",", list)); // 42,16,32,2,9,-7,1,0,0
list.Reverse(1, 3);
Console.WriteLine(string.Join(",", list)); // 42,2,32,16,9,-7,1,0,0
В статье рассмотрена работа с коллекцией List, рассмотрена базовая работа (инициализация, обращение к данными) и методы для работы со списком:
- Добавление.
- Поиск.
- Удаление.
- Сортировка.
- Работа с диапазонами.
Комментарии