Книга: C# 4.0: полное руководство

Классы обобщенных коллекций

Классы обобщенных коллекций

Как упоминалось ранее, классы обобщенных коллекций по большей части соответствуют своим необобщенным аналогам, хотя в некоторых случаях они носят другие имена. Отличаются они также своей организацией и функциональными возможностями. Классы обобщенных коллекций определяются в пространстве имен System.Collections.Generic. В табл. 25.14 приведены классы, рассматриваемые в этой главе. Эти классы составляют основу обобщенных коллекций.

Таблица 25.14. Основные классы обобщенных коллекций

Класс - Описание

Dictionary<Tkey, TValue> - Сохраняет пары “ключ-значение". Обеспечивает такие же функциональные возможности, как и необобщенный класс Hashtable

HashSet<T> - Сохраняет ряд уникальных значений, используя хеш-таблицу

LinkedList<T> - Сохраняет элементы в двунаправленном списке

List<T> - Создает динамический массив. Обеспечивает такие же функциональные возможности, как и необобщенный класс ArrayList

Queue<T> - Создает очередь. Обеспечивает такие же функциональные возможности, как и необобщенный класс Queue

SortedDictionary<TKey, TValue> - Создает отсортированный список из пар “ключ-значение"

SortedList<TKey, TValue> - Создает отсортированный список из пар “ключ-значение”. Обеспечивает такие же функциональные возможности, как и необобщенный класс SortedList

SortedSet<T> - Создает отсортированное множество

Stack<T> - Создает стек. Обеспечивает такие же функциональные возможности, как и необобщенный класс Stack

-------------------------------

ПРИМЕЧАНИЕ

В пространстве имен System.Collections.Generic находятся также следующие классы: класс SynchronizedCollection<T> синхронизированной коллекции на основе класса IList<T>; класс SynchronizedReadOnlyCollection<T>, доступной только для чтения синхронизированной коллекции на основе класса lList<T>; абстрактный класс SynchronizedKeyCollectionc<T> служащий в качестве базового для класса коллекции System.ServiceModel.UriSchemeKeyedCollection; а также класс KeyedByTypeCollection<T> коллекции, в которой в качестве ключей используются отдельные типы данных.

-------------------------------

Класс List<T>

В классе List<T> реализуется обобщенный динамический массив. Он ничем принципиально не отличается от класса необобщенной коллекции ArrayList. В этом классе реализуются интерфейсы ICollection, ICollection<T>, IList, IList<T>, IEnumerable и IEnumerable<T>. У класса List<T> имеются следующие конструкторы.

public List()
public List(IEnumerable<T> collection)
public List(int capacity)

Первый конструктор создает пустую коллекцию класса List с выбираемой по умолчанию первоначальной емкостью. Второй конструктор создает коллекцию типа List с количеством инициализируемых элементов, которое определяется параметром collection и равно первоначальной емкости массива. Третий конструктор создает коллекцию типа List, имеющую первоначальную емкость, задаваемую параметром capacity. В данном случае емкость обозначает размер базового массива, используемого для хранения элементов коллекции. Емкость коллекции, создаваемой в виде динамического массива, может увеличиваться автоматически по мере добавления в нее элементов.

В классе List<T> определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Некоторые из наиболее часто используемых методов этого класса перечислены в табл. 25.15.

Таблица 25.15. Наиболее часто используемые методы, определенные в классе List<T>

Метод - Описание

public virtual void AddRange(Icollection collection) - Добавляет элементы из коллекции collection в конец вызывающей коллекции типа ArrayList

public virtual int BinarySearch(T item) - Выполняет поиск в вызывающей коллекции значения, задаваемого параметром item. Возвращает индекс совпавшего элемента. Если искомое значение не найдено, возвращается отрицательное значение. Вызывающий список должен быть отсортирован

public int BinarySearch(T item, IComparer<T> comparer) - Выполняет поиск в вызывающей коллекции значения, задаваемого параметром item, используя для сравнения указанный способ, определяемый параметром comparer. Возвращает индекс совпавшего элемента. Если искомое значение не найдено, возвращается отрицательное значение. Вызывающий список должен быть отсортирован

public int BinarySearch(int index, int count,T item, IComparer<T> comparer) - Выполняет поиск в вызывающей коллекции значения, задаваемого параметром item, используя для сравнения указанный способ, определяемый параметром comparer. Поиск начинается с элемента, указываемого по индексу index, и включает количество элементов, определяемых параметром count. Метод возвращает индекс совпавшего элемента. Если искомое значение не найдено, возвращается отрицательное значение. Вызывающий список должен быть отсортирован

public List<T> GetRange(int index, int count) - Возвращает часть вызывающей коллекции. Часть возвращаемой коллекции начинается с элемента, указываемого по индексу index, и включает количество элементов, задаваемое параметром count. Возвращаемый объект ссылается на те же элементы, что и вызывающий объект

public int IndexOf(T item) - Возвращает индекс первого вхождения элемента item в вызывающей коллекции. Если искомый элемент не обнаружен, возвращается значение -1

public void InsertRange(int index, IEnumerable<T> collection) - Вставляет элементы коллекции collection в вызывающую коллекцию, начиная с элемента, указываемого по индексу index

public int LastlndexOf(T item) - Возвращает индекс последнего вхождения элемента item в вызывающей коллекции. Если искомый элемент не обнаружен, возвращается значение -1

public void RemoveRange(int index, int count) - Удаляет часть вызывающей коллекции, начиная с элемента, указываемого по индексу index, и включая количество элементов, определяемое параметром count

public void Reverse() - Располагает элементы вызывающей коллекции в обратном порядке

public void Reverse(int index, int count) - Располагает в обратном порядке часть вызывающей коллекции, начиная с элемента, указываемого по индексу index, и включая количество элементов, определяемое параметром count

public void Sort() - Сортирует вызывающую коллекцию по нарастающей

public void Sort(IComparer<T> comparer) - Сортирует вызывающую коллекцию, используя для сравнения способ, задаваемый параметром comparer. Если параметр comparer имеет пустое значение, то для сравнения используется способ, выбираемый по умолчанию

public void Sort(Comparison<T> comparison) - Сортирует вызывающую коллекцию, используя для сравнения указанный делегат

public void Sort(int index, int count,IComparer<T> comparer) - Сортирует вызывающую коллекцию, используя для сравнения способ, задаваемый параметром comparer. Сортировка начинается с элемента, указываемого по индексу index, и включает количество элементов, определяемых параметром count. Если параметр comparer имеет пустое значение, то для сравнения используется способ, выбираемый по умолчанию

public T [ ] ToArray() - Возвращает массив, содержащий копии элементов вызывающего объекта

public void TrimExcess() - Сокращает емкость вызывающей коллекции таким образом, чтобы она не превышала 10% от количества элементов, хранящихся в ней на данный момент

В классе List<T> определяется также собственное свойство Capacity, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Это свойство объявляется следующим образом.

public int Capacity { get; set; }

Свойство Capacity позволяет установить и получить емкость вызывающей коллекции в качестве динамического массива. Эта емкость равна количеству элементов, которые может содержать коллекция до ее вынужденного расширения. Такая коллекция расширяется автоматически, и поэтому задавать ее емкость вручную необязательно. Но из соображений эффективности это иногда можно сделать, если заранее известно количество элементов коллекции. Благодаря этому исключаются издержки на выделение дополнительной памяти.

В классе List<T> реализуется также приведенный ниже индексатор, определенный в интерфейсе IList<T>.

public Т this[int index] { get; set; }

С помощью этого индексатора устанавливается и получается значение элемента коллекции, указываемое по индексу index.

В приведенном ниже примере программы демонстрируется применение класса List<T>. Это измененный вариант примера, демонстрировавшего ранее класс ArrayList. Единственное изменение, которое потребовалось для этого, заключалось в замене класса ArrayList классом List, а также в использовании параметров обобщенного типа.

// Продемонстрировать применение класса List<T>.
using System;
using System.Collections.Generic;
class GenListDemo {
  static void Main() {
    // Создать коллекцию в виде динамического массива.
    List<char> lst = new List<char>();
    Console.WriteLine("Исходное количество элементов: " + lst.Count);
    Console.WriteLine();
    Console.WriteLine("Добавить 6 элементов");
    // Добавить элементы в динамический массив.
    lst.Add('С');
    lst.Add('А');
    lst.Add('Е');
    lst.Add('В');
    lst.Add('D');
    lst.Add('F');
    Console.WriteLine("Количество элементов: " + lst.Count);
    // Отобразить содержимое динамического массива,
    // используя индексирование массива.
    Console.Write("Текущее содержимое: ");
    for (int i=0; i < lst.Count;. i++)
      Console.Write(lst[i] + " ");
    Console.WriteLine("n");
    Console.WriteLine("Удалить 2 элемента ");
    // Удалить элементы из динамического массива.
    lst.Remove('F');
    lst.Remove('А');
    Console.WriteLine("Количество элементов: " + lst.Count);
    // Отобразить содержимое динамического массива, используя цикл foreach.
    Console.Write("Содержимое: ");
    foreach(char с in lst)
      Console.Write(с + " ");
    Console.WriteLine("n");
    Console.WriteLine("Добавить еще 20 элементов");
    // Добавить количество элементов, достаточное для
    // принудительного расширения массива,
    for(int i=0; i < 20; i++)
      lst.Add((char) ('a' + i));
    Console.WriteLine("Текущая емкость: " + lst.Capacity);
    Console.WriteLine("Количество элементов после добавления 20 новых: " + lst.Count);
    Console.Write("Содержимое: ");
    foreach(char с in lst)
      Console.Write(с + " ") ;
    Console.WriteLine("n");
    // Изменить содержимое динамического массива,
    // используя индексирование массива.
    Console.WriteLine("Изменить три первых элемента");
    lst[0] = 'X';
    lst[1] = 'Y' ;
    lst[2] = 'Z';
    Console.Write("Содержимое: ");
    foreach(char с in lst)
      Console.Write(с + " ");
    Console.WriteLine();
    // Следующая строка кода недопустима из-за
    // нарушения безопасности обобщенного типа.
    // lst.Add(99); // Ошибка, поскольку это не тип char!
  }
}

Эта версия программы дает такой же результат, как и предыдущая.

Исходное количество элементов: 0
Добавить 6 элементов
Количество элементов: 6
Текущее содержимое: С А Е В D F
Удалить 2 элемента
Количество элементов: 4
Содержимое: С Е В D
Добавить еще 20 элементов
Текущая емкость: 32
Количество элементов после добавления 20 новых: 24
Содержимое: С Е В D a b c d e f g h i j k l m n o p q r s t
Изменить три первых элемента
Содержимое: X Y Z D a b c d e f g h i j k l m n o p q r s t

Класс LinkedList<T>

В классе LinkedList<T> создается коллекция в виде обобщенного двунаправленного списка. В этом классе реализуются интерфейсы ICollection, ICollection<T>, IEnumerable, IEnumerable<T>, ISerializable и IDeserializationCallback. В двух последних интерфейсах поддерживается сериализация списка. В классе LinkedList<T> определяются два приведенных ниже открытых конструктора.

public LinkedListO
public LinkedList(IEnumerable<T> collection)

В первом конструкторе создается пустой связный список, а во втором конструкторе — список, инициализируемый элементами из коллекции collection.

Как и в большинстве других реализаций связных списков, в классе LinkedList<T> инкапсулируются значения, хранящиеся в узлах списка, где находятся также ссылки на предыдущие и последующие элементы списка. Эти узлы представляют собой объекты класса LinkedListNode<T>. В классе LinkedListNode<T> предоставляются четыре следующих свойства.

public LinkedListNode<T> Next { get; }
public LinkedListNode<T> Previous { get; }
public LinkedList<T> List { get; }
public T Value { get; set; }

С помощью свойств Next и Previous получаются ссылки на предыдущий и последующий узлы списка соответственно, что дает возможность обходить список в обоих направлениях. Если же предыдущий или последующий узел отсутствует, то возвращается пустая ссылка. Для получения ссылки на сам список служит свойство List. А с помощью свойства Value можно устанавливать и получать значение, находящееся в узле списка.

В классе LinkedList<T> определяется немало методов. В табл. 25.16 приведены наиболее часто используемые методы данного класса. Кроме того, в классе LinkedList<T> определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже.

public LinkedListNode<T> First { get; }
public LinkedListNode<T> Last { get; }

С помощью свойства First получается первый узел в списке, а с помощью свойства Last — последний узел в списке.

Таблица 25.16. Наиболее часто используемые методы, определенные в классе LinkedList<T>

Метод - Описание

public LinkedListNode<T> AddAfter(LinkedListNode<T> node, T value) - Добавляет в список узел со значением value непосредственно после указанного узла node. Указываемый узел node не должен быть пустым (null). Метод возвращает ссылку на узел, содержащий значение value

public void AddAfter(LinkedListNode<T> node,LinkedListNode<T> newNode) - Добавляет в список новый узел newNode непо

средственно после указанного узла node. Указываемый узел node не должен быть пустым (null). Если узел node отсутствует в списке или если новый узел newNode является частью другого списка, то* генерируется исключение InvalidOperationException

public LinkedListNode<T> AddBefore(LinkedListNode<T> node,T value) - Добавляет в список узел со значением value непо

средственно перед указанным узлом node. Указываемый узел node не должен быть пустым (null). Метод возвращает ссылку на узел, содержащий значение value

public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode) - Добавляет в список новый узел newNode не

посредственно перед указанным узлом node. Указываемый узел node не должен быть пустым (null). Если узел node отсутствует в списке или если новый узел newNode является частью другого списка, то генерируется исключение InvalidOperationException

public LinkedList<T> AddFirst(T value) - Добавляет узел со значением value в начало списка. Метод возвращает ссылку на узел, содержащий значение value

public void AddFirst(LinkedListNode node) - Добавляет узел node в начало списка. Если узел node является частью другого списка, то генерируется исключение InvalidOperationException

public LinkedList<T> AddLast(T value) - Добавляет узел со значением value в конец списка. Метод возвращает ссылку на узел, содержащий значение value

public void AddLast(LinkedListNode node) - Добавляет узел node в конец списка. Если узел node является частью другого списка, то генерируется исключение InvalidOperationException

public LinkedList<T> Find(T value) - Возвращает ссылку на первый узел в списке, имеющий значение value. Если искомое значение value отсутствует в списке, то возвращается пустое значение

public LinkedList<T> FindLast(T value) - Возвращает ссылку на последний узел в списке, имеющий значение value. Если искомое значение value отсутствует в списке, то возвращается пустое значение

public bool Remove(T value) - Удаляет из списка первый узел, содержащий значение value. Возвращает логическое значение true, если узел удален, т.е. если узел со значением value обнаружен в списке и удален; в противном случае возвращает логическое значение false

public void Remove(LinkedList<T> node) - Удаляет из списка узел, соответствующий указанному узлу node. Если узел node отсутствует в списке, то генерируется исключение InvalidOperationException

public void RemoveFirst() - Удаляет из списка первый узел

public void RemoveLast() - Удаляет из списка последний узел

В приведенном ниже примере программы демонстрируется применение класса LinkedList<T>.

// Продемонстрировать применение класса LinkedList<T>.
using System;
using System.Collections.Generic;
class GenLinkedListDemo {
  static void Main() {
    // Создать связный список.
    LinkedList<char> ll = new LinkedList<char>();
    Console.WriteLine("Исходное количество элементов в списке: " + ll.Count);
    Console.WriteLine();
    Console.WriteLine("Добавить в список 5 элементов");
    // Добавить элементы в связный список.
    ll.AddFirst('А');
    ll.AddFirst('В');
    ll.AddFirst('С');
    ll.AddFirst('D');
    ll.AddFirst('Е');
    Console.WriteLine("Количество элементов в списке: " + ll.Count);
    // Отобразить связный список, обойдя его вручную.
    LinkedListNode<char> node;
    Console.Write("Отобразить содержимое списка по ссылкам: ");
    for(node = ll.First; node != null; node = node.Next)
      Console.Write(node.Value + " ");
    Console.WriteLine("n") ;
    // Отобразить связный список, обойдя его в цикле foreach.
    Console.Write("Отобразить содержимое списка в цикле foreach: ");
    foreach(char ch in ll)
      Console.Write(ch + " ");
    Console.WriteLine("n");
    // Отобразить связный список, обойдя его
    // вручную в обратном направлении.
    Console.Write("Следовать по ссылкам в обратном направлении: ");
    for(node = ll.Last; node != null; node = node.Previous)
      Console.Write(node.Value + " ");
    Console.WriteLine ("n");
    // Удалить из списка два элемента.
    Console.WriteLine("Удалить 2 элемента из списка");
    // Удалить элементы из связного списка.
    ll.Remove('С');
    ll.Remove('А');
    Console.WriteLine("Количество элементов в списке: " + ll.Count);
    // Отобразить содержимое видоизмененного списка в цикле foreach.
    Console.Write("Содержимое списка после удаления элементов: ");
    foreach(char ch in ll)
      Console.Write(ch + " ");
    Console.WriteLine ("n");
    // Добавить три элемента в конец списка.
    ll.AddLast('X');
    ll.AddLast('Y');
    ll.AddLast('Z');
    Console.Write("Содержимое списка после ввода элементов: ");
    foreach(char ch in ll)
      Console.Write(ch + " ");
    Console.WriteLine("n");
  }
}

Ниже приведен результат выполнения этой программы.

Исходное количество элементов в списке: 0
Добавить в список 5 элементов
Количество элементов в списке: 5
Отобразить содержимое списка по ссылкам: Е D С В А
Отобразить содержимое списка в цикле foreach: Е D С В А
Следовать по ссылкам в обратном направлении: А В С D Е
Удалить 2 элемента из списка
Количество элементов в списке: 3
Содержимое списка после удаления элементов: Е D В
Содержимое списка после ввода элементов: Е D В X Y Z

Самое примечательное в этой программе — это обход списка в прямом и обратном направлении, следуя по ссылкам, предоставляемым свойствами Next и Previous. Двунаправленный характер подобных связных списков имеет особое значение для приложений, управляющих базами данных, где нередко требуется перемещаться по списку в обоих направлениях.

Класс DictionaryCTKey, TValue>

Класс Dictionary<TKey, TValue> позволяет хранить пары "ключ-значение" в коллекции как в словаре. Значения доступны в словаре по соответствующим ключам. В этом отношении данный класс аналогичен необобщенному классу Hashtable. В классе Dictionary<TKey, TValue> реализуются интерфейсы IDictionary, IDictionary<TKey, TValue>, ICollection, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable, IEnumerable<KeyValuePair<TKey, TValue>>, ISerializable и IDeserializationCallback. В двух последних интерфейсах поддерживается сериализация списка. Словари имеют динамический характер, расширяясь по мере необходимости.

В классе Dictionary<TKey, TValue> предоставляется немало конструкторов. Ниже перечислены наиболее часто используемые из них.

public Dictionary()
public Dictionary(IDictionaryCTKey, TValue> dictionary)
public Dictionary(int capacity)

В первом конструкторе создается пустой словарь с выбираемой по умолчанию первоначальной емкостью. Во втором конструкторе создается словарь с указанным количеством элементов dictionary. А в третьем конструкторе с помощью параметра capaci ty указывается емкость коллекции, создаваемой в виде словаря. Если размер словаря заранее известен, то, указав емкость создаваемой коллекции, можно исключить изменение размера словаря во время выполнения, что, как правило, требует дополнительных затрат вычислительных ресурсов.

В классе Dictionary<TKey, TValue> определяется также ряд методов. Некоторые наиболее часто используемые методы этого класса сведены в табл. 25.17.

Таблица 25.17. Наиболее часто используемые методы, определенные в классе Die tionaryCTKey, TValue>

Метод - Описание

public void Add(TKey key, TValue value) - Добавляет в словарь пару “ключ-значение", определяемую параметрами key и value. Если ключ key уже находится в словаре, то его значение не изменяется, и генерируется исключение ArgumentException

public bool ContainsKey(TKey key) - Возвращает логическое значение true, если вызывающий словарь содержит объект key в качестве ключа; а иначе — логическое значение false

public bool ContainsValue(TValue value) - Возвращает логическое значение true, если вызывающий словарь содержит значение value; в противном случае — логическое значение false

public bool Remove(TKey key) - Удаляет ключ key из словаря. При удачном исходе операции возвращается логическое значение true, а если ключ key отсутствует в словаре — логическое значение false

Кроме того, в классе Dictionary<TKey, TValue> определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже.

Свойство - Описание

public IEqualityComparer<TKey> Comparer { get; } - Получает метод сравнения для вызывающего словаря

public Dictionary<TKey, TValue>. KeyCollection Keys { get; } - Получает коллекцию ключей

public Dictionary<TKey, TValue>. ValueCollection Values { get; } - Получает коллекцию значений

Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступны отдельными списками с помощью свойств Keys и Values. В коллекциях типа DictionaryCTKey, TValue>.KeyCollection и Dictionary<TKey, TValue>.ValueCollection> реализуются как обобщенные, так и необобщенные формы интерфейсов ICollection и IEnumerable.

И наконец, в классе DictionaryCTKey, TValue> реализуется приведенный ниже индексатор, определенный в интерфейсе IDictionary<TKey, TValue>

public TValue this[TKey key] { get; set; }

Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в качестве индекса в данном случае служит ключ элемента, а не сам индекс.

При перечислении коллекции типа DictionaryCTKey, TValue> из нее возвращаются пары "ключ-значение" в форме структуры KeyValuePairCTKey, TValue> Напомним, что в этой структуре определяются два поля.

public TKey Key;
public TValue Value;

В этих полях содержится ключ или значение соответствующего элемента коллекции. Как правило, структура KeyValuePairCTKey, TValue> не используется непосредственно, поскольку средства класса DictionaryCTKey, TValue> позволяют работать с ключами и значениями по отдельности. Но при перечислении коллекции типа Dictionary<TKey, TValue>, например, в цикле foreach перечисляемыми объектами являются пары типа KeyValuePair.

Все ключи в коллекции типа DictionaryCTKey, TValue> должны быть уникальными, причем ключ не должен изменяться до тех пор, пока он служит в качестве ключа. В то же время значения не обязательно должны быть уникальными. К тому же объекты не хранятся в коллекции типа DictionaryCTKey, TValue> в отсортированном порядке.

В приведенном ниже примере демонстрируется применение класса DictionaryCTKey, TValue>
// Продемонстрировать применение класса обобщенной
// коллекции DictionaryCTKey, TValueX
using System;
using System.Collections.Generic;
class GenDictionaryDemo {
  static void Main() {
    // Создать словарь для хранения имен и фамилий
    // работников и их зарплаты.
    Dictionary<string, double> diet =
            new Dictionary<string, double>();
    // Добавить элементы в коллекцию,
    diet.Add("Батлер, Джон", 73000);
    diet.Add("Шварц, Capa", 59000);
    diet.Add("Пайк, Томас", 45000);
    diet.Add("Фрэнк, Эд", 99000);
    // Получить коллекцию ключей, т.е. фамилий и имен.
    ICollection<string> с = diet.Keys;
    // Использовать ключи для получения значений, т.е. зарплаты,
    foreach(string str in с)
      Console.WriteLine ("{0}, зарплата: {1:C}", str, diet[str]);
  }
}

Ниже приведен результат выполнения этой программы.

Батлер, Джон, зарплата: $73 000,00

Шварц, Capa, зарплата: $59 000,00

Пайк, Томас, зарплата: $45 000,00

Фрэнк, Эд, зарплата: $99 000,00

Класс SortedDictionary<TKey, TValue>

В коллекции класса SortedDictionary<TKey, TValue> пары "ключ-значение" хранятся таким же образом, как и в коллекции класса Dictionary<TKey, TValue>, за исключением того, что они отсортированы по соответствующему ключу. В классе SortedDictionary<TKey, TValue> реализуются интерфейсы IDictionary, IDictionary<TKey, TValue>, ICollection, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable и IEnumerable<KeyValuePair<TKey, TValue>>. В классе SortedDictionary<TKey, TValue> предоставляются также следующие конструкторы.

public SortedDictionary()
public SortedDictionary(IDictionary<TKey, TValue> dictionary)
public SortedDictionary(IComparer<TKey> comparer)
public SortedDictionary(IDictionaryCTKey, TValue> dictionary,
                   IComparer<TKey> comparer)

В первом конструкторе создается пустой словарь, во втором конструкторе — словарь с указанным количеством элементов dictionary. В третьем конструкторе допускается указывать с помощью параметра comparer типа IComparer способ сравнения, используемый для сортировки, а в четвертом конструкторе — инициализировать словарь, помимо указания способа сравнения.

В классе SortedDictionary<TKey, TValue> определен ряд методов. Некоторые наиболее часто используемые методы этого класса сведены в табл. 25.18.

Таблица 25.18. Наиболее часто используемые методы, определенные в классе SortedDictionaryCTKey, TValue>

Метод - Описание

public void Add (TKey key, TValue value) -

Добавляет в словарь пару “ключ-значение", определяемую параметрами key и value. Если ключ key уже находится в словаре, то его значение не изменяется, и генерируется исключение ArgumentException

public bool ContainsKey (TKey кеу) - Возвращает логическое значение true, если вызывающий словарь содержит объект key в качестве ключа; в противном случае — логическое значение false

public bool ContainsValue(TValue value)public bool Remove(TKey key) - Возвращает логическое значение true, если вызывающий словарь содержит значение value; в противном случае — логическое значение false Удаляет ключ key из словаря. При удачном исходе операции возвращается логическое значение true, а если ключ key отсутствует в словаре — логическое значение false

Кроме того, в классе SortedDictionary<TKey, TValue> определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже.

Свойство - Описание

public Icomparer<TKey> Comparer { get; } - Получает метод сравнения для вызывающего словаря

public SortedDictionary<TKey, TValue>.KeyCollection Keys { get; } - Получает коллекцию ключей

public SortedDictionary<TKey, TValue>.ValueCollection Values { get; } - Получает коллекцию значений

Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступны отдельными списками с помощью свойств Keys и Values. В коллекциях типа SortedDictionary<TKey, TValue>.KeyCollection и SortedDictionary<TKey, TValu*e>.ValueCollection реализуются как обобщенные, так и необобщенные формы интерфейсов ICollection и IEnumerable.

И наконец, в классе SortedDictionary<TKey, TValue> реализуется приведенный ниже индексатор, определенный в интерфейсе IDictionary<TKey, TValue>
public TValue this[TKey key] { get; set; }

Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в данном случае в качестве индекса служит ключ элемента, а не сам индекс.

При перечислении коллекции типа SortedDictionary<TKey, TValue> из нее возвращаются пары "ключ-значение" в форме структуры KeyValuePair<TKey, TValue> Напомним, что в этой структуре определяются два следующих поля.

public TKey Key; public TValue Value;

В этих полях содержится ключ или значение соответствующего элемента коллекции. Как правило, структура KeyValuePair<TKey, TValue> не используется непосредственно, поскольку средства класса SortedDictionary<TKey, TValue> позволяют работать с ключами и значениями по отдельности. Но при перечислении коллекции типа SortedDictionary<TKey, TValue>, например в цикле foreach, перечисляемыми объектами являются пары типа KeyValuePair.

Все ключи в коллекции типа SortedDictionary<TKey, TValue> должны быть уникальными, причем ключ не должен изменяться до тех пор, пока он служит в качестве ключа. В то же время значения не обязательно должны быть уникальными.

В приведенном ниже примере демонстрируется применение класса SortedDictionary<TKey, TValue> Это измененный вариант предыдущего примера, демонстрировавшего применение класса Dictionary<TKey, TValue> В данном варианте база данных работников отсортирована по фамилии и имени работника, которые служат в качестве ключа.

// Продемонстрировать применение класса обобщенной
// коллекции SortedDictionary<TKey, TValue>
using System;
using System.Collections.Generic;
class GenSortedDictionaryDemo {
  static void Main() {
    // Создать словарь для хранения имен и фамилий
    // работников и их зарплаты.
    SortedDictionary<string, double> diet =
          new SortedDictionary<string, double>();
    // Добавить элементы в коллекцию,
    diet.Add("Батлер, Джон", 73000);
    diet.Add("Шварц, Capa", 59000);
    diet.Add("Пайк, Томас", 45000);
    diet.Add("Фрэнк, Эд", 99000);
    // Получить коллекцию ключей, т.е. фамилий и имен.
    ICollection<string> с = diet.Keys;
    // Использовать ключи для получения значений, т.е. зарплаты,
    foreach(string str in с)
      Console.WriteLine("{0}, зарплата: {1:C}", str, diet[str]);
  }
}

Эта программа дает следующий результат.

Батлер, Джон, зарплата: $73,000.00

Пайк, Томас, зарплата: $45,000.00

Фрэнк, Эд, зарплата: $99,000.00

Шварц, Сара, зарплата: $59,000.00

Как видите, список работников и их зарплаты отсортированы по ключу, в качестве которого в данном случае служит фамилия и имя работника.

Класс SortedList<TKey, TValue>

В коллекции класса SortedList<TKey, TValue> хранится отсортированный список пар "ключ-значение". Это обобщенный эквивалент класса необобщенной коллекции SortedList. В классе SortedList<TKey, TValue> реализуются интерфейсы IDictionary, IDictionary<TKey, TValue>, ICollection, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable и IEnumerable<KeyValuePair<TKey, TValue>>. Размер коллекции типа SortedList<TKey, TValue> изменяется динамически, автоматически увеличиваясь по мере необходимости. Класс SortedList<TKey, TValue> подобен классу SortedDictionary<TKey, TValue>, но у него другие рабочие характеристики. В частности, класс SortedList<TKey, TValue> использует меньше памяти, тогда как класс SortedDictionary<TKey, TValue> позволяет быстрее вставлять неупорядоченные элементы в коллекцию.

В классе SortedListcTKey, TValue> предоставляется немало конструкторов. Ниже перечислены наиболее часто используемые конструкторы этого класса.

public SortedList()
public SortedList(IDictionaryCTKey, TValue> dictionary)
public SortedList(int capacity)
public SortedList(IComparer<TK> comparer)

В первой форме конструктора создается пустой список с выбираемой по умолчанию первоначальной емкостью. Во второй форме конструктора создается отсортированный список с указанным количеством элементов dictionary. В третьей форме конструктора с помощью параметра capacity задается емкость коллекции, создаваемой в виде отсортированного списка. Если размер списка заранее известен, то, указав емкость создаваемой коллекции, можно исключить изменение размера списка во время выполнения, что, как правило, требует дополнительных затрат вычислительных ресурсов. И в четвертой форме конструктора допускается указывать с помощью параметра comparer способ сравнения объектов, содержащихся в списке.

Емкость коллекции типа SortedList<TKey, TValue> увеличивается автоматически по мере необходимости, когда в список добавляются новые элементы. Если текущая емкость коллекции превышается, то она увеличивается. Преимущество указания емкости коллекции типа SortedList<TKey, TValue> при ее создании заключается в снижении или полном исключении издержек на изменение размера коллекции. Разумеется, указывать емкость коллекции целесообразно лишь в том случае, если заранее известно, сколько элементов требуется хранить в ней.

В классе SortedList<TKey, TValue> определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Некоторые из наиболее часто используемых методов этого класса перечислены в табл. 25.19. Следует иметь в виду, что перечислитель, возвращаемый методом GetEnumerator(), служит для перечисления пар "ключ-значение", хранящихся в отсортированном списке в виде объектов типа KeyValuePair.

Таблица 25.19. Наиболее часто используемые методы, определенные в классе SortedListCTKey, TValue>

Метод -     Описание

public void Add (TKey    key,TValue value) -  Добавляет в список пару “ключ-значение", определяемую параметрами key и value. Если ключ key уже находится в списке, то его значение не изменяется, и генерируется исключение ArgumentException

public bool ContainsKey (ТК key) Возвращает логическое значение true, если вызывающий список содержит объект key в каче-_стве ключа; а иначе логическое значение false

public bool ContainsValue(TValue value) - Возвращает логическое значение true, если вызывающий список содержит значение value; в противном случае — логическое значение false

public IEnumerator<KeyValuePair CTKey, TValue>> GetEnumerator() - Возвращает перечислитель для вызывающего словаря

 public int IndexOfKey(TKey key) - Возвращает индекс ключа key. Если искомый ключ не обнаружен в списке, возвращается значение -1

public int IndexOfValue(TValue value) - Возвращает индекс первого вхождения значения value в вызывающем списке. Если искомое значение не обнаружено в списке, возвращается значение -1

public bool Remove(TKey key) - Удаляет из списка пару “ключ-значение” по указанному ключу key. При удачном исходе операции возвращается логическое значение true, а если ключ key отсутствует в списке — логическое значение false

public void RemoveAt(int index) - Удаляет из списка пару “ключ-значение” по указанному индексу index

public void TrimExcess() -  Сокращает избыточную емкость вызывающей коллекции в виде отсортированного списка

Кроме того, в классе SortedList<TK, TV> определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже.

Свойство - Описание

public int Capacity { get; set; } - Получает или устанавливает емкость вызывающей коллекции в виде отсортированного списка

public IComparer<TK> Comparer { get; } - Получает метод сравнения для вызывающего списка

public IList<TK> Keys { get; } - Получает коллекцию ключей

public IList<TV> Values { get; } - Получает коллекцию значений

И наконец, в классе SortedList<TKey, TValue> реализуется приведенный ниже индексатор, определенный в интерфейсе IDictionaryCTKey, TValue>
public TValue this[TKey key] { get; set; }

Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в данном случае в качестве индекса служит ключ элемента, а не сам индекс.

В приведенном ниже примере демонстрируется применение класса SortedList<TKey, TValue> Это еще один измененный вариант представленного ранее примера базы данных работников. В данном варианте база данных хранится в коллекции типа SortedList.

// Продемонстрировать применение класса обобщенной
// коллекции SortedList<TKey, TValue>.
using System;
using System.Collections.Generic;
class GenSLDemo {
  static void Main() {
    // Создать коллекцию в виде отсортированного списка
    // для хранения имен и фамилий работников и их зарплаты.
    SortedList<string, double> sl =
         new SortedList<string, double>();
    // Добавить элементы в коллекцию,
    sl.Add("Батлер, Джон", 73000);
    sl.Add("Шварц, Capa", 59000);
    sl.Add("Пайк, Томас", 45000);
    sl.Add("Фрэнк, Эд", 99000);
    // Получить коллекцию ключей, т.е. фамилий и имен.
    ICollection<string> с = sl.Keys;
    // Использовать ключи для получения значений, т.е. зарплаты,
    foreach(string str in с)
      Console.WriteLine("{0}, зарплата: {1:C}", str, sl[str]);
    Console.WriteLine();
  }
}

Ниже приведен результат выполнения этой программы.

Батлер, Джон, зарплата: $73,000.00
Пайк, Томас, зарплата: $45,000.00
Фрэнк, Эд, зарплата: $99,000.00
Шварц, Сара, зарплата: $59,000.00

Как видите, список работников и их зарплаты отсортированы по ключу, в качестве которого в данном случае служит фамилия и имя работника.

Класс Stack<T>

Класс Stack<T> является обобщенным эквивалентом класса необобщенной коллекции Stack. В нем поддерживается стек в виде списка, действующего по принципу "первым пришел — последним обслужен". В этом классе реализуются интерфейсы Collection, IEnumerable и IEnumerable<T>. Кроме того, в классе Stack<T> непосредственно реализуются методы Clear(),Contains() и СоруТо(), определенные в интерфейсе ICollection<T>. А методы Add() и Remove() в этом классе не поддерживаются, как, впрочем, и свойство IsReadOnly. Коллекция класса Stack<T> имеет динамический характер, расширяясь по мере необходимости, чтобы вместить все элементы, которые должны в ней храниться. В классе Stack<T> определяются следующие конструкторы.

public Stack()
public Stack(int capacity)
public Stack(IEnumerable<T> collection)

В первой форме конструктора создается пустой стек с выбираемой по умолчанию первоначальной емкостью, а во второй форме — пустой стек, первоначальный размер которого определяет параметр capacity. И в третьей форме создается стек, содержащий элементы коллекции, определяемой параметром collection. Его первоначальная емкость равна количеству указанных элементов.

В классе Stack<T> определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются, а также в интерфейсе ICollection<T>. Некоторые из наиболее часто используемых методов этого класса перечислены в табл.25.20. Как и в классе Stack, эти методы обычно применяются следующим образом. Для того чтобы поместить объект на вершине стека, вызывается метод Push(). А для того чтобы извлечь и удалить объект из вершины стека, вызывается метод Pop(). Если же объект требуется только извлечь, но не удалить из вершины стека, то вызывается метод Реек(). А если вызвать метод Pop() или Реек(), когда вызывающий стек пуст, то сгенерируется исключение InvalidOperationException.

Таблица 25.20. Методы, определенные в классе Stack<T>

Метод - Описание

public T Peek() - Возвращает элемент, находящийся на вершине стека, но не удаляет его

public T Pop() - Возвращает элемент, находящийся на вершине стека, удаляя его в процессе работы

public void Push(T item) - Помещает элемент item в стек

Public T[] ToArray() - Возвращает массив, содержащий копии элементов вызывающего стека

public void TrimExcess() - Сокращает избыточную емкость вызывающей коллекции в виде стека

В приведенном ниже примере программы демонстрируется применение класса Stack<T>.

// Продемонстрировать применение класса Stack<T>.
using System;
using System.Collections.Generic;
class GenStackDemo {
  static void Main() {
    Stack<string> st = new Stack<string>();
    st.Push("один");
    st.Push("два");
    st.Push("три");
    st.Push("четыре");
    st.Push("пять");
    while(st.Count > 0) {
      string str = st.Pop();
      Console.Write(str + " ");
    }
    Console.WriteLine();  }
}

При выполнении этой программы получается следующий результат.

пять четыре три два один

Класс Queue<T>

Класс Queue<T> является обобщенным эквивалентом класса необобщенной коллекции Queue. В нем поддерживается очередь в виде списка, действующего по принципу "первым пришел — первым обслужен". В этом классе реализуются интерфейсы ICollection, IEnumerable и IEnumerable<T>. Кроме того, в классе Queue<T> непосредственно реализуются методы Clear(), Contains() и CopyTo(), определенные в интерфейсе ICollection<T>. А методы Add() и Remove() в этом классе не поддерживаются, как, впрочем, и свойство IsReadOnly. Коллекция класса Queue<T> имеет динамический характер, расширяясь по мере необходимости, чтобы вместить все элементы, которые должны храниться в ней. В классе Queue<T> определяются следующие конструкторы.

public Queue()
public Queue(int capacity)
public Queue(IEnumerable<T> collection)

В первой форме конструктора создается пустая очередь с выбираемой по умолчанию первоначальной емкостью, а во второй форме — пустая очередь, первоначальный размер которой определяет параметр capacity. И в третьей форме создается очередь, содержащая элементы коллекции, определяемой параметром collection. Ее первоначальная емкость равна количеству указанных элементов.

В классе Queue<T> определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются, а также в интерфейсе ICollection<T>. Некоторые из наиболее часто используемых методов этого класса перечислены в табл.25.21. Как и в классе Queue, эти методы обычно применяются следующим образом. Для того чтобы поместить объект в очередь, вызывается метод Enqueue(). Если требуется извлечь и удалить первый объект из начала очереди, то вызывается метод Dequeue(). Если же требуется извлечь, но не удалять следующий объект из очереди, то вызывается метод Реек(). А если методы Dequeue() и Реек() вызываются, когда очередь пуста, то генерируется исключение InvalidOperationException.

Таблица 25.21. Методы, определенные в классе Queue<T>

Метод -  Описание

public T Dequeue() - Возвращает объект из начала вызывающей    очереди. Возвращаемый объект удаляется из очереди

public void Enqueue (Т item) -  Добавляет элемент item в конец очереди

public T Реек() - Возвращает элемент из начала вызывающей очере_ди, но не удаляет его

public virtual Т[] ToArray() - Возвращает массив, который содержит копии элементов из вызывающей очереди

public void TrimExcess() -  Сокращает избыточную емкость вызывающей коллекции в виде очереди

В приведенном ниже примере демонстрируется применение класса Queue<T>.

// Продемонстрировать применение класса Queue<T>.
using System;
using System.Collections.Generic;
class GenQueueDemo {
  static void Main() {
    Queue<double> q = new Queue<double>();
    q.Enqueue(98.6);
    q.Enqueue(212.0);
    q.Enqueue(32.0);
    q.Enqueue(3.1416);
    double sum = 0.0;
    Console.Write("Очередь содержит: ");
    while(q.Count > 0) {
      double val = q.Dequeue();
      Console.Write(val + " ");
      sum += val;.
    }
    Console.WriteLine("nИтоговая сумма равна " +• sum);
  }
}

Вот к какому результату приводит выполнение этой программы.

Очередь содержит: 98.6 212 32 3.1416
Итоговая сумма равна 345.7416

Класс HashSet<T>

В классе HashSet<T> поддерживается коллекция, реализующая множество. Для хранения элементов этого множества в нем используется хеш-таблица. В классе HashSet<T> реализуются интерфейсы ICollection<T>, ISet<T>, IEnumerable, IEnumerable<T>, ISerializable, а также IDeserializationCallback. В коллекции типа HashSet<T> реализуется множество, все элементы которого являются уникальными. Иными словами, дубликаты в таком множестве не допускаются. Порядок следования элементов во множестве не указывается. В классе HashSet<T> определяется полный набор операций с множеством, определенных в интерфейсе I$et<T>, включая пересечение, объединение и разноименность. Благодаря этому класс HashSet<T> оказывается идеальным средством для работы с множествами объектов, когда порядок расположения элементов во множестве особого значения не имеет. Коллекция типа HashSet<T> имеет динамический характер и расширяется по мере необходимости, чтобы вместить все элементы, которые должны в ней храниться.

Ниже перечислены наиболее употребительные конструкторы, определенные в классе HashSet<T>.

public HashSet()
public HashSet(IEnumerable<T> collection)
public HashSet(IEqualityCompare comparer)
public HashSet(IEnumerable<T> collection, IEqualityCompare comparer)

В первой форме конструктора создается пустое множество, а во второй форме — множество, состоящее из элементов указываемой коллекции collection. В третьей форме конструктора допускается указывать способ сравнения с помощью параметра comparer. А в четвертой форме создается множество, состоящее из элементов указываемой коллекции collection, и используется заданный способ сравнения comparer. Имеется также пятая форма конструктора данного класса, в которой допускается инициализировать множество последовательно упорядоченными данными.

В классе HashSet<T> реализуется интерфейс ISet<T>, а следовательно, в нем предоставляется полный набор операций со множествами. В этом классе предоставляется также метод RemoveWhere(), удаляющий из множества элементы, не удовлетворяющие заданному условию, или предикату.

Помимо свойств, определенных в интерфейсах, которые реализуются в классе HashSet<T>, в него введено дополнительное свойство Comparer, приведенное ниже.

public IEqualityComparer<T> Comparer { get; }

Оно позволяет получать метод сравнения для вызывающего хеш-множества.

Ниже приведен конкретный пример применения класса HashSet<T>.

// Продемонстрировать применение класса HashSet<T>.
using System;
using System.Collections.Generic;
class HashSetDemo {
  static void Show(string msg, HashSet<char> set) {
    Console.Write(msg);
    foreach(char ch in set)
      Console.Write(ch + " ");
    Console.WriteLine();
  }
  static void Main() {
    HashSet<char> setA = new HashSet<char>();
    HashSet<char> setB = new HashSet<char>();
    setA.Add('A');
    setA.Add('В');
    setA.Add('C');
    setB.Add('C');
    setB.Add('D');
    setB.Add('Е');
    Show("Исходное содержимое множества setA: ", setA);
    Show("Исходное содержимое множества setB: ", setB);
    setA.SymmetricExceptWith(setB);
    Show("Содержимое множества setA после " +
        "разноименности со множеством SetB: ", setA);
    setA.UnionWith(setB);
    Show("Содержимое множества setA после " +
        "объединения со множеством SetB: ", setA);
    setA.ExceptWith(setB);
    Show("Содержимое множества setA после " +
        "вычитания из множества setB: ", setA);
    Console.WriteLine();
  }
}

Ниже приведен результат выполнения программы из данного примера.

Исходное содержимое множества setA: A B C

Исходное содержимое множества setB: С D Е

Содержимое множества setA после разноименности со множеством SetB: А В D Е

Содержимое множества setA после объединения со множеством SetB: А В D Е С

Содержимое множества setA после вычитания из множества setB: А В

Класс SortedSet<T>

Класс SortedSet<T> представляет собой новую разновидность коллекции, введенную в версию 4.0 среды .NET Framework. В нем поддерживается коллекция, реализующая отсортированное множество. В классе SortedSet<T> реализуются интерфейсы ISet<T>, ICollection, ICollection<T>, IEnumerable, IEnumerable<T>, ISerializable, а также IDeserializationCallback. В коллекции типа SortedSet<T> реализуется множество, все элементы которого являются уникальными. Иными словами, дубликаты в таком множестве не допускаются. В классе SortedSet<T> определяется полный набор операций с множеством, определенных в интерфейсе ISet<T>, включая пересечение, объединение и разноименность. Благодаря тому что все элементы коллекции типа SortedSet<T> сохраняются в отсортированном порядке, класс SortedSet<T> оказывается идеальным средством для работы с отсортированными множествами объектов. Коллекция типа SortedSet<T> имеет динамический характер и расширяется по мере необходимости, чтобы вместить все элементы, которые должны в ней храниться.

Ниже перечислены четыре наиболее часто используемые конструктора, определенных в классе SortedSet<T>.

public SortedSetO
public SortedSet(IEnumerable<T> collection)
public SortedSet(IComparer comparer)
public SortedSet(IEnumerable<T> collection, IComparer comparer)

В первой форме конструктора создается пустое множество, а во второй форме — множество, состоящее из элементов указываемой коллекции collection. В третьей форме конструктора допускается указывать способ сравнения с помощью параметра comparer. А в четвертой форме создается множество, состоящее из элементов указываемой коллекции collection, и используется заданный способ сравнения comparer. Имеется также пятая форма конструктора данного класса, в которой допускается инициализировать множество последовательно упорядоченными данными.

В классе SortedSet<T> реализуется интерфейс ISet<T>, а следовательно, в нем предоставляется полный набор операций со множествами. В этом классе предоставляется также метод GetViewBetween(), возвращающий часть множества в форме объекта типа SortedSet<T>, метод RemoveWhere(), удаляющий из множества элементы, не удовлетворяющие заданному условию, или предикату, а также метод Reverse(), возвращающий объект типа IEnumerable<T>, который циклически проходит множество в обратном порядке.

Помимо свойств, определенных в интерфейсах, которые реализуются в классе SortedSet<T>, в него введены дополнительные свойства, приведенные ниже.

public IComparer<T> Comparer { get; }
public T Max { get; }
public T Min { get; }

Свойство Comparer получает способ сравнения для вызывающего множества. Свойство Мах получает наибольшее значение во множестве, а свойство Min — наименьшее значение во множестве.

В качестве примера применения класса SortedSet<T> на практике просто замените обозначение HashSet на SortedSet в исходном коде программы из предыдущего подраздела, посвященного коллекциям типа HashSet<T>.

Оглавление книги


Генерация: 1.030. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз