Хотя в языке 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 и один элемент задается при инициализации.
Начиная с версии C# 12 для определения списков можно использовать выражения коллекций, которые предполагают заключение элементов коллекции в квадратные скобки:
List<string> people = ["Tom", "Bob", "Sam"]; List<string> employees = [];// пустой список
Подобным образом можно работать со списками других типов, например:
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" });
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" };