Коллекции в C#
Коллекция или контейнер это объект программы, который содержит экземпляры наборов данных и который позволяет управлять этой коллекцией. Под управлением коллекцией понимается выполнение добавления в коллекцию нового экземпляра, исключения экземпляра из коллекции, просмотр содержимого экземпляров, получение информации о количестве экземпляров в коллекции и т. д.
Перечисленные операции не зависят от типа данных, используемого в контейнере, но позволяют сократить затраты труда на написание программ для реализации этих операций. Для доступа к контейнеру достаточно добавить сборку System.Collections.Generic, содержащую методы для работы с коллекциями.
Контейнеры можно разделить на две группы:
• Последовательные контейнеры.
• Ассоциативные множества (словари).
Ниже указаны основные последовательные контейнеры:
• Список ArrayList необобщенный список. Объявляется без лексемы обобщения (<T>).
• Список List -обобщенный вариант класса ArrayList и
• Связный список (класс LinkedList).
Последовательные контейнеры различаются по способу вставки и исключения экземпляров наборов данных. Контейнер типа списка соответствует понятию одномерного массива в языке программирования, в котором элементы расположены в памяти компьютера строго последовательно, и обращение к элементам выполняется по индексу. Для вставки элемента в середину такого массива потребуется освободить место, занятое уже существующим элементом, а для этого надо переместить все элементы, которые должны следовать после вставляемого элемента на одну позицию по направлению к концу массива. При больших размерах массивов такая операция занимает значительное время. Аналогично для исключения элемента надо на его место переписать элемент, следующий за ним и т. д. С другой стороны, время доступа к элементу минимально, поскольку по индексу можно сразу вычислить адрес элемента в памяти.
В связных списках каждый элемент имеет указатель на следующий элемент, поэтому элементы в памяти могут размещаться в произвольном порядке, а, следовательно, для вставки элемента в середину списка требуется выделить для нового элемента память и настроить указатели так, чтобы новый элемент оказался в требуемом месте. Поэтому в связных списках время вставки (и исключения) элементов минимально (не зависит ни от размера списка, ни от места вставки), но для доступа к элементу данных требуется последовательный проход элементов, предшествующих искомому, что требует времени.
Независимо от типа и вида контейнера интерфейс для работы с контейнером стандартизован в том смысле, что одинаковые по назначению операции и параметры являются одинаковыми для всех контейнеров.
В коллекциях может применяться приведенная ниже конструкция:
«член класса» = default(T);
Такая конструкция обеспечивает инициализацию членов класса значениями по умолчанию.
Для всех коллекций вводится понятие перечисляемого типа данных, который поддерживается как в необобщенных интерфейсах IEnumerator и IEnumerable, так и в обобщенных интерфейсах IEnumerator<T> и IEnumerable<T>. Перечисляемый тип данных предоставляет возможность доступа к элементам коллекции. В каждой коллекции должен быть реализован обобщенный или необобщенный интерфейс IEnumerable, поэтому элементы любого класса коллекции должны быть доступны посредством методов, определенных в интерфейсе IEnumerator или IEnumerator<T>.
Необобщенный интерфейс позволяет орбрабатывать в цикле разнотипные элементы, для которых строится перечисляемый тип данных.
Это означает, что, внеся минимальные изменения в код циклического обращения к коллекции одного типа, его можно использовать для аналогичного обращения к коллекции другого типа.
Для поочередного обращения к содержимому коллекции в цикле foreach используется перечисляемый тип данных. С перечисляемым типом данных непосредственно связано другое средство, называемое итератором. Это средство упрощает процесс создания классов коллекций, например специальных, поочередное обращение к которым организуется в цикле foreach.
Ниже приведен пример программы для обработки обобщенного списка List. Элементом списка является класс List, содержащий 4 поля. В программе рассмотрено:
• Создание списка.
• Просмотр списка (вывод на экран).
• Сортировка списка (по умолчанию).
• Сортировка списка по заданному полю.
• Поиск элемента с заданным значением поля.
• Подсчет количества элементов с заданным значением поля.
• Фильтрация элементов списка.
Список создается из файла, в одной строке значения для одного элемента списка.
class Program
{
class Block: IComparable<Block> // Подключение интерфейса
{ // Описание класса
public string prof { get; set; }
public string name { get; set; }
public int skil;
public double wage;
public Block() { } // Пустой конструктор
public Block(string p, string s, int k, double w) // Конструктор с параметрами
{
prof = p;
name = s;
skil = k;
wage = w;
}
public int CompareTo(Block obj)
{
Block blk;
blk = (Block) obj;
int n = this.prof.CompareTo(blk.prof); // Сравнение по полю prof
return n;
}
public class sortOnSkill : IComparer<Block> // Для переключения поля
{ // Имя sortOnSkil произвольное, для каждого поля сравнения свое имя
public int Compare(Block a, Block b)
{
if (a.skil > b.skil) return 1; // Сравнение по полю skil
else if (a.skil < b.skil) return -1;
else return 0;
}
}
}
static void Main(string[] args)
{ // Файл поместить в папку Debug, либо прописывать полный путь
StreamReader file =
new StreamReader("C:\Temp\blocks.txt", Encoding.Default);
string s;
int sk;
double wg;
string[ ] str;
Block blk = new Block();
List<Block> lst = new List<Block>() ;
// Формирование списка из файла
while ((s = file.ReadLine()) != null)
{
str = s.Split(;); // Расщепление строки по точке с запятой(str - массив)
sk = int.Parse(str[2]); // Преобразование строки в число
wg = double.Parse(str[3]);
lst.Add(new Block(str[0],str[1], sk, wg)); // Добавить в блок
}
Console.WriteLine("Начальный список");
foreach (Block bl in lst) // Вывод начального списка
Console.WriteLine("{0,-12} {1,-8} {2} {3}", bl.prof, bl.name, bl.skil, bl.wage);
Console.WriteLine("
Сортировка по профессии");
lst.Sort(); // Сортировка по умолчанию
foreach (Block bl in lst) // Вывод сортированного списка
Console.WriteLine("{0,-12} {1,-8} {2} {3}",
bl.prof, bl.name, bl.skil,bl.wage);
Console.WriteLine("
Сортировка по квалификации");
// Вызов класса, изменяющего поле для сортировки
IComparer<Block> comp = new ArrayLst.Program.Block.sortOnSkill();
lst.Sort(comp);
foreach (Block bl in lst) // Вывод сортированного списка
Console.WriteLine("{0,-12} {1,-8} {2} {3}", bl.prof, bl.name, bl.skil, bl.wage);
Console.WriteLine("
Поиск");
blk = lst.Find(le => le.prof == "токарь" && le.name == "Иванов");
Console.WriteLine("{0,-12} {1,-8} {2} {3}",
blk.prof, blk.name, blk.skil, blk.wage);
Console.WriteLine("
Подсчет количества выбранных элементов");
int count = 0;
string fstr = blk.prof;// Блок blk был найден при поиске
foreach (Block bl in lst)
{
if (bl.prof == fstr) count++;// Количество блоков с заданным элементом
}
Console.WriteLine("Количество элементов "{0}" = {1}",fstr, count);
Console.WriteLine("
Выборка с помощью фильтра");
// Список flst для размещения отфильтрованных блоков
List<Block> flst = new List<Block>();
double fwg = lst[1].wage;// Выбрана зарплата
foreach (Block bl in lst)
{ // Фильтрация
if (bl.wage == fwg) flst.Add(new Block(bl.prof, bl.name, bl.skil, bl.wage));
}
foreach (Block bl in flst)
{ // Просмотр результатов фильтрации
Console.WriteLine("{0,-12} {1,-8} {2} {3}",
bl.prof, bl.name, bl.skil, bl.wage);
}
Console.ReadKey();
}
Не отлажена, ошибка здесь
//IComparer<Block> comp = new List<Block.sortOnSkill>();
IEnumerable<Block> comp = new List<Block.sortOnSkill>();
lst.Sort(comp);
Ниже представлен результат выполнения программы.
Классы Queue и Stack также являются последовательными контейнерами, но изменяют доступ к элементам контейнера для получения специализированных списков (очередей и стеков). Для обращения к элементам этих классов существуют специальные команды.
Для стека добавление элемента выполняется командой Push, просмотр командой Peek, и команда Pop извлекает элемент из списка.
static void Main(string[] args)
{
Stack<string> stc = new Stack<string>(); // Пустой стек
stc.Push("First"); // Добавление элемента в стек
stc.Push("Second");
stc.Push("Third");
stc.Push("Fourth");
Console.WriteLine("Вершина стека {0}
", stc.Peek());
string st = stc.Pop();// Извлечение в строку с извлечением из списка
Console.WriteLine("Извлечь с помощью Pop {0}"
+"
после команды Pop на вершине стека {1}", st,stc.Peek() );
Console.ReadKey();
}
Пример выполнения программы.
Для очереди добавление элемента выполняется командой Enqueue, а просмотр вершины очереди командой Peek, и команда Dequeue извлекает элемент из списка.
static void Main(string[] args)
{
Queue <string> qu = new Queue<string>();// Пустая очередь
qu.Enqueue("First"); // Добавление элемента в очередь
qu.Enqueue("Second");
qu.Enqueue("Third");
qu.Enqueue("Fourth");
Console.WriteLine("Вершина очереди {0}
", qu.Peek());
string st = qu.Dequeue();// Извлечение в строку с извлечением из списка
Console.WriteLine("Команда Dequeue извлекла: {0}"
+ "
после команды Dequeue на вершине очереди {1}", st,qu.Peek());
Console.ReadKey();
}
Пример выполнения программы.
При объявлении обобщенных контейнеров можно задать ограничения на классов или структур. Эти ограничения задаются с помощью ключевого слова where и позволяют учесть
В обобщенных контейнерах в качестве параметра указывается, по крайней мере, один тип данных. Платформа .NET позволяет помощью ключевого слова where наложить ограничение на тип данных, обрабатываемый в контейнере.
where T : struct Тип данных <T> должен быть структурой)
where T : class Тип данных <T> должен быть ссылочным типом
where T : new() Тип данных <T> должен иметь конструктор с параметрами. Это ограничение должно указываться последним.
where T : ИмяБазовогоКласса Тип данных <T> должен наследоваться от указанного класса.
where T : ИмяИнтерфейса Тип данных <T> должен реализовать, указанный интерфейс.
Из перечисленных ограничений наиболее важным является where T: new(), так как это требование на наличие конструктора с параметрами, и компилятор напомнит программисту о необходимости переопределения такого конструктора в объявлении класса. Ниже приведен пример
// Класс MyClass конструктор с параметрами
public class MyClass<T> where T : new()
{
}
Коллекции в C#
Лекции по предмету «Программирование»