2. Колекції. Простір імен System.Collections
У С# під колекцією розуміється деяка група об'єктів. Класи-колекції спрощують реалізацію багатьох завдань програмування, пропонуючи вже готові рішення для побудови структур даних. Всі колекції розроблені на основі певних інтерфейсів, тому стандартизують спосіб обробки групи об'єктів. Середовище .NET Framework підтримує три основні типи колекцій: загального призначення, спеціалізовані і орієнтовані на побітову організацію даних.
У просторі імен System.Collections визначені набори стандартних колекцій і інтерфейсів, які реалізовані в цих колекціях.
Простір імен System.Collections.Specialized [спешелайз] включає спеціалізовані колекції, наприклад, колекцію рядків StringCollection і хеш-таблицю із строковими ключами StringDictionary.
У таблиці 1 перелічені основні колекції, визначені в просторі System.Collections та основні інтерфейси, які в них реалізовані.
Таблиця 1. Класи колекцій загального призначення (з простору імен System.Collections)
Клас |
Призначення |
Найважливіші з реалізованих інтерфейсів |
ArrayList |
Динамічний масив |
IList, ICollection, IEnumerable, ICloneable |
BitArray |
Компактний масив для зберігання бітових значень |
ICollection, IEnumerable, ICloneable |
Hashtable |
Хеш-таблиця |
IDictionary, ICollection, IEnumerable, ICloneable |
Queue |
Черга |
ICollection, ICloneable, IEnumerable |
SortedList |
Колекція, відсортована по ключах. Доступ до елементів — по ключу або по індексу |
IDictionary, ICollection, IEnumerable, ICloneable |
Stack |
Стек |
ICollection, IEnumerable |
3. Універсальні колекції. Простір імен System.Collections.Generic
У другу версію бібліотеки .NET додані універсальні колекції для представлення цих структур даних. Ці колекції, розташовані у просторі імен System.Collections.Generic, дублюють аналогічні колекції простору імен System.Collections. Сьогодні рекомендується використовувати універсальні колекції скрізь, де потрібно замість звичайних колекцій, тому що вони працюють ефективніше (не потребують операцій упаковки, розпаковки даних).
У таблиці 2 наведено відповідність між звичайними і універсальними колекціями бібліотеки .NET.
Таблиця 2. Універсальні колекції System.Collections.Generic
Універсальний клас (версія 2.0) |
Звичайний клас |
Інтерфейси |
Comparer<T> Базовий клас для реалізацій універсального інтерфейсу IComparer<T> |
Comparer
|
IComparer<T>. |
List<T> однозв’язний список |
ArrayList |
IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
|
List<T> |
BitArray |
IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
|
Dictionary<K,T> словник |
HashTable |
|
Queue<T> черга |
Queue |
|
LinkedList<T> двозвязний список
|
Не має неуніверсального еквіваленту |
|
SortedDictionary<K,T> |
SortedList |
|
Stack<T> стек |
Stack |
|
Повний перелік класів простору імен System.Collections.Generic :
https://msdn.microsoft.com/ru-ru/library/system.collections.generic(v=vs.110).aspx
Таблиця 3. Універсальні інтерфейси
Інтерфейс |
Опис |
ICollection<T> |
Визначає методи, використовувані для управління універсальними колекціями. |
IComparer<T> |
Визначає метод, що реалізується типом для порівняння двох об'єктів. |
IDictionary<TKey, TValue> |
Представляє універсальну колекцію пар ключ/значення. |
IEnumerable<T> |
Надає нумератор, який підтримує простий перебір елементів у вказаній колекції. |
IEnumerator<T> |
Підтримує простий перебір елементів універсальної колекції. |
IEqualityComparer<T> |
Визначає методи для підтримки операцій порівняння об'єктів відносно рівності. |
IList<T> |
Представляє колекцію об'єктів, доступ до яких можна дістати окремо, за індексом. |
IReadOnlyCollection<T> |
Представляє доступну тільки для читання колекцію елементів. |
IReadOnlyDictionary<TKey, TValue> |
Представляє універсальну колекцію пар "ключ-значення", доступну тільки для читання. |
IReadOnlyList<T> |
Представляє доступну тільки для читання колекцію об'єктів, доступ до яких можна дістати за індексом. |
ISet<T> |
Надає основний інтерфейс для абстракції наборів. |
4. Універсальний клас List<>
Клас List<T> є універсальним еквівалентом класу ArrayList.
Реалізує універсальний інтерфейс IList<T> за допомогою масиву, розмір якого динамічно збільшується за потреби.
Таблиця 4. Конструктори класу List<T>
https://msdn.microsoft.com/ru-ru/library/6sh2ey19(v=vs.110).aspx
List<T>() |
Створює новий порожній екземпляр класу List<T> з початковою ємністю за замовчанням. |
List<T>(IEnumerable<T>) |
Створює новий екземпляр List<T>, який містить елементи, скопійовані з вказаної колекції, і має місткість, достатню для розміщення усіх скопійованих елементів. |
List<T>(Int32) |
Створює новий порожній екземпляр класу List<T> з вказаною початковою ємністю . |
Таблиця 5. Властивості класу List<T>
https://msdn.microsoft.com/ru-ru/library/6sh2ey19(v=vs.110).aspx
Властивість |
Опис |
Capacity |
Повертає або задає загальне число елементів, які може вмістити внутрішня структура даних без зміни розміру. |
Count |
Отримує число елементів, що містяться в List<T>. |
Item |
Отримує або задає елемент з вказаним індексом. |
Таблиця 6. Основні методи класу List<T>
https://msdn.microsoft.com/ru-ru/library/6sh2ey19(v=vs.110).aspx
Ім'я |
Опис |
Add |
Додає об'єкт в кінець колекції List<T>. |
AddRange |
Додає елементи вказаної колекції в кінець списку List<T>. |
BinarySearch(T) |
Виконує пошук елементу за усім відсортованим списком List<T>, використовуючи порівняння за замовчанням, і повертає індекс елементу, відлічуваний від нуля. |
BinarySearch(T, IComparer<T>) |
Виконує пошук елементу за усім відсортованим списком List<T>, використовуючи вказаний компаратор, і повертає індекс елементу, відлічуваний від нуля. |
BinarySearch(Int32, Int32, T, IComparer<T>) |
Виконує пошук елементу в діапазоні елементів відсортованого списку List<T>, використовуючи вказаний метод порівняння, і повертає індекс елементу, відлічуваний від нуля. |
Clear |
Видаляє усі елементи з колекції List<T>. |
Contains |
Визначає, чи входить елемент до складу List<T>. |
ConvertAll<TOutput> |
Перетворює елементи поточного списку List<T> у інший тип і повертає список перетворених елементів. |
CopyTo(T[]) |
Копіює увесь список List<T> у сумісний одновимірний масив, починаючи з першого елементу цільового масиву. |
CopyTo(T[], Int32) |
Копіює List<T> цілком в сумісний одновимірний масив, починаючи з вказаного індексу кінцевого масиву. |
CopyTo(Int32, T[], Int32, Int32) |
Копіює діапазон елементів із списку List<T> у сумісний одновимірний масив, починаючи з вказаного індексу кінцевого масиву. |
Equals(Object) |
Визначає, чи рівний заданий об'єкт поточному об'єкту. (Успадковано від Object.) |
Exists |
Визначає, чи містить List<T> елементи, що задовольняють умовам вказаного предиката. |
Finalize |
Дозволяє об'єкту спробувати звільнити ресурси і виконати інші операції очищення, перш ніж об'єкт утилізує в процесі зборки сміття. (Успадковано від Object.) |
Find |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає перше знайдене входження в межах усього списку List<T>. |
FindAll |
Вибирає всі елементи, що задовольняють умовам вказаного предиката. |
FindIndex(Predicate<T>) |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає відлічуваний від нуля індекс першого знайденого входження в межах усього списку List<T>. |
FindIndex(Int32, Predicate<T>) |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає відлічуваний від нуля індекс першого входження в діапазоні елементів списку List<T>, починаючи із заданого індексу і закінчуючи останнім елементом. |
FindIndex(Int32, Int32, Predicate<T>) |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає відлічуваний від нуля індекс першого входження в діапазоні елементів списку List<T>, що починається із заданого індексу і містить вказане число елементів. |
FindLast |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає останнє знайдене входження в межах усього списку List<T>. |
FindLastIndex(Predicate<T>) |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає відлічуваний від нуля індекс останнього знайденого входження в межах усього списку List<T>. |
FindLastIndex(Int32, Predicate<T>) |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає відлічуваний від нуля індекс останнього входження в діапазоні елементів списку List<T>, починаючи з першого елементу і закінчуючи елементом із заданим індексом. |
FindLastIndex(Int32, Int32, Predicate<T>) |
Виконує пошук елементу, вказаного предиката, що задовольняє умовам, і повертає відлічуваний від нуля індекс останнього входження в діапазоні елементів списку List<T>, що містить вказане число елементів і закінчується елементом із заданим індексом. |
ForEach |
Виконує вказану дію з кожним елементом списку List<T>. |
GetEnumerator |
Повертає нумератор, що здійснює перебір елементів списку List<T>. |
GetHashCode |
Грає роль хеш-функції для певного типу.(Успадковано від Object.) |
GetRange |
Створює неповну копію діапазону елементів початкового списку List<T>. |
GetType |
Повертає об'єкт Type для поточного екземпляра. (Успадковано від Object.) |
IndexOf(T) |
Здійснює пошук вказаного об'єкту і повертає відлічуваний від нуля індекс першого входження, знайденого в межах усього списку List<T>. |
IndexOf(T, Int32) |
Здійснює пошук вказаного об'єкту і повертає відлічуваний від нуля індекс першого входження в діапазоні елементів списку List<T>, починаючи із заданого індексу і до останнього елементу. |
IndexOf(T, Int32, Int32) |
Виконує пошук вказаного об'єкту і повертає відлічуваний від нуля індекс першого входження в діапазоні елементів списку List<T>, що починається із заданого індексу і містить вказане число елементів. |
Insert |
Додає елемент у список List<T> у позиції з вказаним індексом. |
InsertRange |
Вставляє елементи колекції в список List<T> у позиції з вказаним індексом. |
Remove |
Видаляє перше входження вказаного об'єкту з колекції List<T>. |
RemoveAll |
Видаляє усі елементи, що задовольняють умовам вказаного предиката. |
RemoveAt |
Видаляє елемент списку List<T> з вказаним індексом. |
RemoveRange |
Видаляє діапазон елементів із списку List<T>. |
Reverse() |
Змінює порядок елементів в усьому списку List<T> на зворотний. |
Reverse(Int32, Int32) |
Змінює порядок елементів у вказаному діапазоні. |
Sort() |
Сортує елементи в усьому списку List<T> за допомогою компаратора за замовчанням. |
Sort(Comparison<T>) |
Сортує елементи в усьому списку List<T> з використанням вказаного делегата System.Comparison<T>. |
Sort(IComparer<T>) |
Сортує елементи в усьому списку List<T> за допомогою вказаного компаратора. |
ToArray |
Копіює елементи списку List<T> у новий масив. |
Приклад 1. https://msdn.microsoft.com/ru-ru/library/6sh2ey19(v=vs.110).aspx
У наступному прикладі демонструється додавання, видалення і вставка простого бізнес-об'єкту в List<T>.
Для роботи зі списком потрібно реалізувати метод Equals, визначений в інтерфейсі IEquatable.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Lab
{
public class Part : IEquatable<Part>
{
public string PartName { get; set; }
public int PartId { get; set; }
public override string ToString()
{
return "ID: " + PartId + " Name: " + PartName;
}
public bool Equals(Part other)
{
if (other == null) return false;
return (this.PartId.Equals(other.PartId));
}
}
public class Example
{
public static void Main()
{
// Create a list of parts.
List<Part> parts = new List<Part>();
// Add parts to the list.
parts.Add(new Part() { PartName = "crank arm", PartId = 1234 });
parts.Add(new Part() { PartName = "chain ring", PartId = 1334 });
parts.Add(new Part() { PartName = "regular seat", PartId = 1434 });
parts.Add(new Part() { PartName = "banana seat", PartId = 1444 });
parts.Add(new Part() { PartName = "cassette", PartId = 1534 });
parts.Add(new Part() { PartName = "shift lever", PartId = 1634 }); ;
// Write out the parts in the list. This will call the overridden ToString method
// in the Part class.
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
// Check the list for part #1734. This calls the IEquitable.Equals method
// of the Part class, which checks the PartId for equality.
Console.WriteLine("\nContains(\"1734\"): {0}",
parts.Contains(new Part { PartId = 1734, PartName = "" }));
// Insert a new item at position 2.
Console.WriteLine("\nInsert(2, \"1834\")");
parts.Insert(2, new Part() { PartName = "brake lever", PartId = 1834 });
//Console.WriteLine();
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
Console.WriteLine("\nParts[3]: {0}", parts[3]);
Console.WriteLine("\nRemove(\"1534\")");
// This will remove part 1534 even though the PartName is different,
// because the Equals method only checks PartId for equality.
parts.Remove(new Part() { PartId = 1534, PartName = "cogs" });
Console.WriteLine();
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
Console.WriteLine("\nRemoveAt(3)");
// This will remove the part at index 3.
parts.RemoveAt(3);
Console.WriteLine();
foreach (Part aPart in parts)
{
Console.WriteLine(aPart);
}
Console.ReadKey();
}
}
}
Приклад 2. Використання класу List<> для зберігання колекції об'єктів класу Person, а також для зберігання цілих чисел.
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
namespace Lab
{
public class Person
{
public string Name; //имя
public int Age; // возраст
public string Role; // роль
public string GetName() { return Name; }
public int GetAge() { return Age; }
public Person(string N, int A)
{
this.Name = N;
this.Age = A;
}
public void Passport()
{
Console.WriteLine("Name = {0} Age = {1}", Name, Age);
}
}
class Program
{
static void Main(string[] args)
{
List<Person> pers = new List<Person>();
pers.Add(new Person("Іванов",18));
pers.Add(new Person("Петров",20));
pers.Add(new Person("Морозов", 3));
foreach (Person x in pers) x.Passport();
List<int> lint = new List<int>();
lint.Add(5); lint.Add(1); lint.Add(3);
lint.Sort();
int а = lint[2];
Console.WriteLine(а);
foreach (int x in lint) Console.Write(x + " ");
Console.ReadLine();
}
}
}
У цьому прикладі створюється дві колекції. Перша (pers) містить елементи класу Person.
Колекція lint складається з цілих чисел, причому для роботи з ними не потрібні явні перетворення типу при отриманні елементу з колекції.
Приклад 3.
https://msdn.microsoft.com/ru-ru/library/6sh2ey19(v=vs.110).aspx
У наступному прикладі показано декілька властивостей і методів універсального класу List<T> строкового типу. За допомогою конструктора за замовчанням створюється список рядків з місткістю за замовчанням. Виводиться значення властивості Capacity, а потім за допомогою методу Add додається декілька елементів. Виводиться список цих елементів, а потім знову виводиться значення властивості Capacity і разом з ним - значення властивості Count, що показує, що місткість збільшується в міру необхідності.
За допомогою методу Contains перевіряється наявність деякого елементу в списку, за допомогою методу Insert в середину списку вставляється новий елемент, після чого знову виводиться вміст списку.
За допомогою властивості за замовчанням Item (індексатор в C#) із списку вибирається елемент, за допомогою методу Remove видаляється перший екземпляр дубльованого елементу, доданий раніше, і вміст списку виводиться знову. Метод Remove завжди видаляє перший виявлений ним екземпляр.
За допомогою методу TrimExcess місткість зменшується відповідно до числа елементів, і виводяться значення властивостей Capacity і Count. Якщо не задіяна місткість складає менше 10% від загальної місткості, змінювати розмір списку не потрібно.
В кінці використовується метод Clear для видалення усіх елементів із списку, і виводяться значення властивостей Capacity і Count.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Lab
{
public class Example
{
public static void Main()
{
List<string> dinosaurs = new List<string>();
Console.WriteLine("\nCapacity: {0}", dinosaurs.Capacity);
dinosaurs.Add("Tyrannosaurus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("Mamenchisaurus");
dinosaurs.Add("Deinonychus");
dinosaurs.Add("Compsognathus");
Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}
Console.WriteLine("\nCapacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);
Console.WriteLine("\nContains(\"Deinonychus\"): {0}",
dinosaurs.Contains("Deinonychus"));
Console.WriteLine("\nInsert(2, \"Compsognathus\")");
dinosaurs.Insert(2, "Compsognathus");
Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}
// Shows accessing the list using the Item property.
Console.WriteLine("\ndinosaurs[3]: {0}", dinosaurs[3]);
Console.WriteLine("\nRemove(\"Compsognathus\")");
dinosaurs.Remove("Compsognathus");
Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}
dinosaurs.TrimExcess();
Console.WriteLine("\nTrimExcess()");
Console.WriteLine("Capacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);
dinosaurs.Clear();
Console.WriteLine("\nClear()");
Console.WriteLine("Capacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);
Console.ReadKey();
}
}
}
5. Пошук заданого елементу у списку List<>
Метод Contains визначає, чи входить елемент у список List<>. У прикладі 1 вище, цей метод використовується для пошуку елемента PartId
// Check the list for part #1734. This calls the IEquitable.Equals method
// of the Part class, which checks the PartId for equality.
Console.WriteLine("\nContains(\"1734\"): {0}",
parts.Contains(new Part { PartId = 1734, PartName = "" }));
Для виконання порівняння на рівність складних об’єктів потрібно зробити клас нащадком інтерфейсу IEquatable <> і реалізувати методи інтерфейсу.
public class Part : IEquatable<Part>
Приклад 4. (модифікований до msdn)
https://msdn.microsoft.com/ru-ru/library/bhkz42b3(v=vs.110).aspx
У наступному прикладі міститься список складних об'єктів типу Box. Клас Box реалізує метод IEquatable<T>.Equals, так, щоб два поля вважалися рівними, якщо їх розміри рівні. В даному прикладі метод Contains повертає значення true, оскільки поле, що має задані виміри, вже знаходиться в колекції.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Lab
{
class Program
{
static void Main(string[] args)
{
List<Box> boxes = new List<Box>();
boxes.Add(new Box(8, 8, 4));
boxes.Add(new Box(8, 4, 8));
boxes.Add(new Box(8, 6, 4));
if (boxes.Contains(new Box(8, 1, 4)))
{
Console.WriteLine("An equal box is already in the collection.");
}
else
{
Console.WriteLine("Box can be added.");
boxes.Add(new Box(8, 1, 4));
}
//Outputs "An equal box is already in the collection."
Console.ReadKey();
}
}
public class Box : IEquatable<Box>
{
public Box(int h, int l, int w)
{
this.Height = h;
this.Length = l;
this.Width = w;
}
public int Height { get; set; }
public int Length { get; set; }
public int Width { get; set; }
public bool Equals(Box other)
{
if (this.Height == other.Height && this.Length == other.Length
&& this.Width == other.Width)
{
return true;
}
else
{
return false;
}
}
}
}
