Список List<T>
Хотя в языке C# есть массивы, которые хранят в себе наборы однотипных объектов, но работать с ними не всегда удобно. Например, массив хранит фиксированное количество объектов, однако что если мы заранее не знаем, сколько нам потребуется объектов. И в этом случае намного удобнее применять коллекции. Еще один плюс коллекций состоит в том, что некоторые из них реализует стандартные структуры данных, например, стек, очередь, словарь, которые могут пригодиться для решения различных специальных задач. Большая часть классов коллекций содержится в пространстве имен System.Collections.Generic.
Класс List<T> из пространства имен System.Collections.Generic представляет простейший список однотипных объектов. Класс List типизируется типом, объекты которого будут хранится в списке.
Мы можем создать пустой список:
List<string> people = new List<string>();
В данном случае объект List типизируется типом string. А это значит, что хранить в этом списке мы можем только строки.
Можно сразу при создании списка инициализировать его начальными значениями. В этом случае элементы списка помещаются после вызова конструктора в фигурных скобках
List<string> people = new List<string>() { "Tom", "Bob", "Sam" };
В данном случае в список помещаются три строки
Также можно при создании списка инициализировать его элементами из другой коллекции, например, другого списка:
var people = new List<string>() { "Tom", "Bob", "Sam" };
var employees = new List<string>(people);
Можно совместить оба способа:
var people = new List<string>() { "Tom", "Bob", "Sam" };
var employees = new List<string>(people){"Mike"};
В данном случае в списке employees будет четыре элемента ({ "Tom", "Bob", "Sam", "Mike" }) - три добавляются из списка people и один элемент задается при инициализации.
Подобным образом можно работать со списками других типов, например:
List<Person> people = new List<Person>()
{
new Person("Tom"),
new Person("Bob"),
new Person("Sam")
};
class Person
{
public string Name { get;}
public Person(string name) => Name = name;
}
Установка начальной емкости списка
Еще один конструктор класса List принимает в качестве параметра начальную емкость списка:
List<string> people = new List<string>(16);
Указание начальной емкости списка позволяет в будущем увеличить производительность и уменьшить издержки на выделение памяти при добавлении элементов. Поскольку динамическое добавление в список может приводить на низком уровне к дополнительному выделению памяти, что снижает производительность. Если же мы знаем, что список не будет превышать некоторый размер, то мы можем передать этот размер в качестве емкости списка и избежать дополнительных выделений памяти.
Также начальную емкость можно установить с помощью свойства Capacity, которое имеется у класса List.
Обращение к элементам списка
Как и массивы, списки поддерживают индексы, с помощью которых можно обратиться к определенным элементам:
var people = new List<string>() { "Tom", "Bob", "Sam" };
string firstPerson = people[0]; // получаем первый элемент
Console.WriteLine(firstPerson); // Tom
people[0] = "Mike"; // изменяем первый элемент
Console.WriteLine(people[0]); // Mike
Длина списка
С помощью свойства Count можно получить длину списка:
var people = new List<string>() { "Tom", "Bob", "Sam" };
Console.WriteLine(people.Count); // 3
Перебор списка
C# позволяет осуществить перебор списка с помощью стандартного цикла foreach:/p>
var people = new List<string>() { "Tom", "Bob", "Sam" };
foreach (var person in people)
{
Console.WriteLine(person);
}
// Вывод программы:
// Tom
// Bob
// Sam
Также можно использовать другие типы циклов и в комбинации с индексами перебирать списки:
var people = new List<string>() { "Tom", "Bob", "Sam" };
for (int i = 0; i < people.Count; i++)
{
Console.WriteLine(people[i]);
}
Методы списка
Среди его методов можно выделить следующие:
void Add(T item): добавление нового элемента в список
void AddRange(IEnumerable<T> collection): добавление в список коллекции или массива
int BinarySearch(T item): бинарный поиск элемента в списке. Если элемент найден, то метод возвращает индекс этого элемента в коллекции. При этом список должен быть отсортирован.
void CopyTo(T[] array): копирует список в массив array
void CopyTo(int index, T[] array, int arrayIndex, int count): копирует из списка начиная с индекса index элементы, количество которых равно count, и вставляет их в массив array начиная с индекса arrayIndex
bool Contains(T item): возвращает true, если элемент item есть в списке
void Clear(): удаляет из списка все элементы
bool Exists(Predicate<T> match): возвращает true, если в списке есть элемент, который соответствует делегату match
T? Find(Predicate<T> match): возвращает первый элемент, который соответствует делегату match. Если элемент не найден, возвращается null
T? FindLast(Predicate<T> match): возвращает последний элемент, который соответствует делегату match. Если элемент не найден, возвращается null
List<T> FindAll(Predicate<T> match): возвращает список элементов, которые соответствуют делегату match
int IndexOf(T item): возвращает индекс первого вхождения элемента в списке
int LastIndexOf(T item): возвращает индекс последнего вхождения элемента в списке
List<T> GetRange(int index, int count): возвращает список элементов, количество которых равно count, начиная с индекса index.
void Insert(int index, T item): вставляет элемент item в список по индексу index. Если такого индекса в списке нет, то генерируется исключение
void InsertRange(int index, collection): вставляет коллекцию элементов collection в текущий список начиная с индекса index. Если такого индекса в списке нет, то генерируется исключение
bool Remove(T item): удаляет элемент item из списка, и если удаление прошло успешно, то возвращает true. Если в списке несколько одинаковых элементов, то удаляется только первый из них
void RemoveAt(int index): удаление элемента по указанному индексу index. Если такого индекса в списке нет, то генерируется исключение
void RemoveRange(int index, int count): параметр index задает индекс, с которого надо удалить элементы, а параметр count задает количество удаляемых элементов.
int RemoveAll((Predicate<T> match)): удаляет все элементы, которые соответствуют делегату match. Возвращает количество удаленных элементов
void Reverse(): изменяет порядок элементов
void Reverse(int index, int count): изменяет порядок на обратный для элементов, количество которых равно count, начиная с индекса index
void Sort(): сортировка списка
void Sort(IComparer<T>? comparer): сортировка списка с помощью объекта comparer, который передается в качестве параметра
Добавление в список
List<string> people = new List<string> () { "Tom" };
people.Add("Bob"); // добавление элемента
// people = { "Tom", "Bob" };
people.AddRange(new[] { "Sam", "Alice" }); // добавляем массив
// people = { "Tom", "Bob", "Sam", "Alice" };
// также можно было бы добавить другой список
// people.AddRange(new List<string>(){ "Sam", "Alice" });
people.Insert(0, "Eugene"); // вставляем на первое место
// people = { "Eugene", "Tom", "Bob", "Sam", "Alice" };
people.InsertRange(1, new string[] {"Mike", "Kate"}); // вставляем массив с индекса 1
// people = { "Eugene", "Mike", "Kate", "Tom", "Bob", "Sam", "Alice" };
// также можно было бы добавить другой список
// people.InsertRange(1, new List<string>(){ "Mike", "Kate" });
</string></string>
Удаление из списка
var people = new List<string> () { "Eugene", "Mike", "Kate", "Tom", "Bob", "Sam", "Tom", "Alice" };
people.RemoveAt(1); // удаляем второй элемент
// people = { "Eugene", "Kate", "Tom", "Bob", "Sam", "Tom", "Alice" };
people.Remove("Tom"); // удаляем элемент "Tom"
// people = { "Eugene", "Kate", "Bob", "Sam", "Tom", "Alice" };
// удаляем из списка все элементы, длина строки которых равна 3
people.RemoveAll(person => person.Length == 3);
// people = { "Eugene", "Kate", "Alice" };
// удаляем из списка 2 элемента начиная с индекса 1
people.RemoveRange(1, 2);
// people = { "Eugene"};
// полностью очищаем список
people.Clear();
// people = { };
Поиск и проверка элемента
var people = new List<string> () { "Eugene", "Mike", "Kate", "Tom", "Bob", "Sam" };
var containsBob = people.Contains("Bob"); // true
var containsBill = people.Contains("Bill"); // false
// проверяем, есть ли в списке строки с длиной 3 символа
var existsLength3 = people.Exists(p => p.Length == 3); // true
// проверяем, есть ли в списке строки с длиной 7 символов
var existsLength7 = people.Exists(p => p.Length == 7); // false
// получаем первый элемент с длиной в 3 символа
var firstWithLength3 = people.Find(p => p.Length == 3); // Tom
// получаем последний элемент с длиной в 3 символа
var lastWithLength3 = people.FindLast(p => p.Length == 3); // Sam
// получаем все элементы с длиной в 3 символа в виде списка
List<string> peopleWithLength3 = people.FindAll(p => p.Length == 3);
// peopleWithLength3 { "Tom", "Bob", "Sam"}
Получение диапазона и копирование в массив
List<string> people = new List<string>() {"Eugene", "Tom", "Mike", "Sam", "Bob" };
// получаем диапазон со второго по четвертый элемент
var range = people.GetRange(1, 3);
// range = { "Tom", "Mike", "Sam"};
// копируем в массив первые три элемента
string[] partOfPeople = new string[3];
people.CopyTo(0, partOfPeople, 0, 3);
// partOfPeople = { "Eugene", "Tom", "Mike"};
Расположение элементов в обратном порядке
var people = new List<string> () { "Eugene", "Tom", "Mike", "Sam", "Bob" };
// переворачиваем весь список
people.Reverse();
// people = { "Bob","Sam", "Mike", "Tom", "Eugene"};
var people2 = new List<string>() { "Eugene", "Tom", "Mike", "Sam", "Bob" };
// переворачиваем часть только 3 элемента с индекса 1
people2.Reverse(1, 3);
// people2 = { "Eugene","Sam", "Mike", "Tom", "Bob" };