Текст книги "Полное руководство. С# 4.0"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 54 (всего у книги 58 страниц)
ГЛАВА 25. Коллекции, перечислители и итераторы
В этой главе речь пойдет об одной из самых важных составляющих среды .NET Framework: коллекци ях. В С# коллекция представляет собой совокупность объектов. В среде .NET Framework имеется немало интер фейсов и классов, в которых определяются и реализуются различные типы коллекций. Коллекции упрощают реше ние многих задач программирования благодаря тому, что предлагают готовые решения для создания целого ряда типичных, но порой трудоемких для разработки структур данных. Например, в среду .NET Framework встроены кол лекции, предназначенные для поддержки динамических массивов, связных списков, стеков, очередей и хеш-таблиц. Коллекции являются современным технологическим сред ством, заслуживающим пристального внимания всех, кто программирует на С#. Первоначально существовали только классы необоб щенных коллекций. Но с внедрением обобщений в версии C# 2.0 среда .NET Framework была дополнена многими новыми обобщенными классами и интерфейсами. Благо даря введению обобщенных коллекций общее количество классов и интерфейсов удвоилось. Вместе с библиотекой распараллеливания задач (TPL) в версии 4.0 среды .NET Framework появился ряд новых классов коллекций, пред назначенных для применения в тех случаях, когда доступ к коллекции осуществляется из нескольких потоков. Нетруд но догадаться, что прикладной интерфейс Collections API составляет значительную часть среды .NET Framework. Кроме того, в настоящей главе рассматриваются два сред ства, непосредственно связанные с коллекциями: перечисли тели и итераторы. И те и другие позволяют поочередно обра щаться к содержимому класса коллекции в цикле foreach. Краткий обзор коллекций
Главное преимущество коллекций заключается в том, что они стандартизируют об работку групп объектов в программе. Все коллекции разработаны на основе набора четко определенных интерфейсов. Некоторые встроенные реализации таких интер фейсов, в том числе ArrayList, Hashtable, Stack и Queue, могут применяться в ис ходном виде и без каких-либо изменений. Имеется также возможность реализовать собственную коллекцию, хотя потребность в этом возникает крайне редко. В среде .NET Framework поддерживаются пять типов коллекций: необобщенные, специальные, с поразрядной организацией, обобщенные и параллельные. Необобщен ные коллекции реализуют ряд основных структур данных, включая динамический мас сив, стек, очередь, а также словари, в которых можно хранить пары "ключ-значение". В отношении необобщенных коллекций важно иметь в виду следующее: они опериру ют данными типа object. Таким образом, необобщенные коллекции могут служить для хранения данных любого типа, причем в одной коллекции допускается наличие разнотипных данных. Очевидно, что такие коллекции не типизированы, поскольку в них хранятся ссылки на данные типа object. Классы и интерфейсы необобщенных коллекций находятся в пространстве имен System.Collections. Специальные коллекции оперируют данными конкретного типа или же делают это каким-то особым образом. Например, имеются специальные коллекции для сим вольных строк, а также специальные коллекции, в которых используется однонаправ ленный список. Специальные коллекции объявляются в пространстве имен System. Collections.Specialized. В прикладном интерфейсе Collections API определена одна коллекция с поразряд ной организацией – это BitArray. Коллекция типа BitArray поддерживает пораз рядные операции, т.е. операции над отдельными двоичными разрядами, например И иди исключающее ИЛИ, а следовательно, она существенно отличается своими воз можностями от остальных типов коллекций. Коллекция типа BitArray объявляется в пространстве имен System.Collections. Обобщенные коллекции обеспечивают обобщенную реализацию нескольких стан дартных структур данных, включая связные списки, стеки, очереди и словари. Такие коллекции являются типизированными в силу их обобщенного характера. Это озна чает, что в обобщенной коллекции могут храниться только такие элементы данных, которые совместимы по типу с данной коллекцией. Благодаря этому исключается случайное несовпадение типов. Обобщенные коллекции объявляются в пространстве имен System.Collections.Generic. Параллельные коллекции поддерживают многопоточный доступ к коллекции. Это обобщенные коллекции, определенные в пространстве имен System.Collections. Concurrent. В пространстве имен System.Collections.ObjectModel находится также ряд классов, поддерживающих создание пользователями собственных обобщенных кол лекций. Основополагающим для всех коллекций является понятие перечислителя, который поддерживается в необобщенных интерфейсах IEnumerator и IEnumerable, а также в обобщенных интерфейсах IEnumerator и IEnumerable. Перечислитель обе спечивает стандартный способ поочередного доступа к элементам коллекции. Следо вательно, он перечисляет содержимое коллекции. В каждой коллекции должна быть реализована обобщенная или необобщенная форма интерфейса IEnumerable, поэто му элементы любого класса коллекции должны быть доступны посредством методов, определенных в интерфейсе IEnumerator или IEnumerator. Это означает, что, внеся минимальные изменения в код циклического обращения к коллекции одного типа, его можно использовать для аналогичного обращения к коллекции другого типа. Любопытно, что для поочередного обращения к содержимому коллекции в цикле foreach используется перечислитель. Основополагающим для всех коллекций является понятие перечислителя, который поддерживается в необобщенных интерфейсах IEnumerator и IEnumerable, а также в обобщенных интерфейсах IEnumerator и IEnumerable. Перечислитель обе спечивает стандартный способ поочередного доступа к элементам коллекции. Следо вательно, он перечисляет содержимое коллекции. В каждой коллекции должна быть реализована обобщенная или необобщенная форма интерфейса IEnumerable, поэто му элементы любого класса коллекции должны быть доступны посредством методов, определенных в интерфейсе IEnumerator или IEnumerator. Это означает, что, внеся минимальные изменения в код циклического обращения к коллекции одного типа, его можно использовать для аналогичного обращения к коллекции другого типа. Любопытно, что для поочередного обращения к содержимому коллекции в цикле foreach используется перечислитель. С перечислителем непосредственно связано другое средство, называемое итерато ром. Это средство упрощает процесс создания классов коллекций, например специ альных, поочередное обращение к которым организуется в цикле foreach. Итераторы также рассматриваются в этой главе. И последнее замечание: если у вас имеется некоторый опыт программирования на C++, то вам, вероятно, будет полезно знать, что классы коллекций по своей сути по добны классам стандартной библиотеки шаблонов (Standard Template Library – STL), определенной в C++. То, что в программировании на C++ называется контейнером, в программировании на C# называется коллекцией. Это же относится и к Java. Если вы знакомы с библиотекой Collections Framework для Java, то научиться пользоваться коллекциями в C# не составит для вас большого труда. В силу характерных отличий каждый из пяти типов коллекций (необобщенных, обобщенных, специальных, с поразрядной организацией и параллельных) будет рас смотрен далее в этой главе отдельно. Необобщенные коллекции Необобщенные коллекции вошли в состав среды .NET Framework еще в версии 1.0. Они определяются в пространстве имен System.Collections. Необобщенные коллек ции представляют собой структуры данных общего назначения, оперирующие ссылка ми на объекты. Таким образом, они позволяют манипулировать объектом любого типа, хотя и не типизированным способом. В этом состоит их преимущество и в то же вре мя недостаток. Благодаря тому что необобщенные коллекции оперируют ссылками на объекты, в них можно хранить разнотипные данные. Это удобно в тех случаях, когда тре буется манипулировать совокупностью разнотипных объектов или же когда типы хра нящихся в коллекции объектов заранее неизвестны. Но если коллекция предназначается для хранения объекта конкретного типа, то необобщенные коллекции не обеспечивают типовую безопасность, которую можно обнаружить в обобщенных коллекциях. Необобщенные коллекции определены в ряде интерфейсов и классов, реализую щих эти интерфейсы. Все они рассматриваются далее по порядку. Интерфейсы необобщенных коллекций В пространстве имен System.Collections определен целый ряд интерфейсов необобщенных коллекций. Начинать рассмотрение необобщенных коллекций следу ет именно с интерфейсов, поскольку они определяют функциональные возможности, которые являются общими для всех классов необобщенных коллекций. Интерфейсы, служащие опорой для необобщенных коллекций, сведены в табл. 25.1. Каждый из этих интерфейсов подробно описывается далее. Таблица 25.1. Интерфейсы необобщенных коллекций Интерфейс ICollection Интерфейс ICollection служит основанием, на котором построены все необобще нные коллекции. В нем объявляются основные методы и свойства для всех необобщен ных коллекций. Он также наследует от интерфейса IEnumerable. В интерфейсе ICollection определяются перечисленные ниже свойства. Свойство Count используется чаще всего, поскольку оно содержит количество эле ментов, хранящихся в коллекции на данный момент. Если значение свойства Count равно нулю, то коллекция считается пустой. В интерфейсе ICollection определяется следующий метод. void СоруТо(Array target, int startIdx) Интерфейс Описание ICollection Определяет элементы, которые должны иметь все необоб щенные коллекции IComparer Определяет метод Compare() для сравнения объектов, хра нящихся в коллекции IDictionary Определяет коллекцию, состоящую из пар "ключ-значение" IDictionaryEnumerator Определяет перечислитель для коллекции, реализующей ин терфейс IDictionary IEnumerable Определяет метод GetEnumerator(), предоставляющий перечислитель для любого класса коллекции IEnumerator Предоставляет методы, позволяющие получать содержимое коллекции по очереди IEqualityComparer Сравнивает два объекта на предмет равенства IHashCodeProvider Считается устаревшим. Вместо него следует использовать ин терфейс IEqualityComparer IList Определяет коллекцию, доступ к которой можно получить с по мощью индексатора IStructuralComparable Определяет метод CompareTo(), применяемый для струк турного сравнения IStructuralEquatable Определяет метод Equals(), применяемый для выяснения структурного, а не ссылочного равенства. Кроме того, опреде ляет метод GetHashCode() Метод СоруТо() копирует содержимое коллекции в массив target, начиная с эле мента, указываемого по индексу startIdx. Следовательно, метод СоруТо() обеспечи вает в C# переход от коллекции к стандартному массиву. Благодаря тому что интерфейс ICollection наследует от интерфейса IEnumerable, в его состав входит также единственный метод, определенный в интерфейсе IEnumerable. Это метод GetEnumerator(), объявляемый следующим образом. IEnumerator GetEnumerator() Он возвращает перечислитель для коллекции. Вследствие того же наследования от интерфейса IEnumerable в интерфей се ICollection определяются также четыре следующих метода расширения: AsParallel(), AsQueryable(), Cast() и OfType(). В частности, метод AsParallel() объявляется в классе System.Linq.ParallelEnumerable, метод AsQueryable() – в классе System.Linq.Queryable, а методы Cast() и OfType() – в классе System. Linq.Enumerable. Эти методы предназначены главным образом для поддержки LINQ, хотя их можно применять и в других целях. Интерфейс IList В интерфейсе IList объявляется такое поведение необобщенной коллекции, ко торое позволяет осуществлять доступ к ее элементам по индексу с отсчетом от нуля. Этот интерфейс наследует от интерфейсов ICollection и IEnumerable. Помимо методов, определенных в этих интерфейсах, в интерфейсе IList определяется ряд собственных методов. Все эти методы сведены в табл. 25.2. В некоторых из них преду сматривается модификация коллекции. Если же коллекция доступна только для чте ния или имеет фиксированный размер, то в этих методах генерируется исключение NotSupportedException. Таблица 25.2. Методы, определенные в интерфейсе IList Свойство Назначение int Count { get; } Содержит количество элементов в коллекции на дан ный момент bool IsSynchronized { get; } Принимает логическое значение true, если коллек ция синхронизирована, а иначе – логическое зна чение false. По умолчанию коллекции не синхро низированы. Но для большинства коллекций можно получить синхронизированный вариант object SyncRoot { get; } Содержит объект, для которого коллекция может быть синхронизирована Метод Описание int Add(object value) Добавляет объект value в вызывающую коллекцию. Возвращает индекс, по которому этот объект сохра няется void Clear() Удаляет все элементы из вызывающей коллекции bool Contains (object value) Возвращает логическое значение true, если вызы вающая коллекция содержит объект value, а ина че – логическое значение false Окончание табл. 25.2 Объекты добавляются в коллекцию типа IList вызовом метода Add(). Обрати те внимание на то, что метод Add() принимает аргумент типа object. А поскольку object является базовым классом для всех типов, то в необобщенной коллекции мо жет быть сохранен объект любого типа, включая и типы значений, в силу автоматиче ской упаковки и распаковки. Для удаления элемента из коллекции служат методы Remove() и RemoveAt(). В част ности, метод Remove() удаляет указанный объект, а метод RemoveAt() удаляет объект по указанному индексу. И для опорожнения коллекции вызывается метод Clear(). Для того чтобы выяснить, содержится ли в коллекции конкретный объект, вызыва ется метод Contains(). Для получения индекса объекта вызывается метод IndexOf(), а для вставки элемента в коллекцию по указанному индексу – метод Insert(). В интерфейсе IList определяются следующие свойства. bool IsFixedSize { get; } bool IsReadOnly { get; } Если коллекция имеет фиксированный размер, то свойство IsFixedSize содержит логическое значение true. Это означает, что в такую коллекцию нельзя ни вставлять элементы, ни удалять их из нее. Если же коллекция доступна только для чтения, то свойство IsReadOnly содержит логическое значение true. Это означает, что содержи мое такой коллекции не подлежит изменению. Кроме того, в интерфейсе IList определяется следующий индексатор. object this[int index] { get; set; } Этот индексатор служит для получения и установки значения элемента коллекции. Но его нельзя использовать для добавления в коллекцию нового элемента. С этой це лью обычно вызывается метод Add(). Как только элемент будет добавлен в коллекцию, он станет доступным посредством индексатора. Метод Описание int IndexOf(object value) Возвращает индекс объекта value, если этот объект содержится в вызывающей коллекции. Если же объект value не обнаружен, то метод возвращает значение -1 void Insert(int index, object value) Вставляет в вызывающую коллекцию объект value по индексу index. Элементы, находившиеся до это го по индексу index и дальше, смещаются вперед, чтобы освободить место для вставляемого объекта value void Remove(object value) Удаляет первое вхождение объекта value в вызыва ющей коллекции. Элементы, находившиеся до этого за удаленным элементом, смещаются назад, чтобы устранить образовавшийся “пробел" void RemoveAt(int index) Удаляет из вызывающей коллекции объект, располо женный по указанному индексу index. Элементы, находившиеся до этого за удаленным элементом, смещаются назад, чтобы устранить образовавшийся “пробел” Интерфейс IDictionary В интерфейсе IDictionary определяется такое поведение необобщенной кол лекции, которое позволяет преобразовать уникальные ключи в соответствующие зна чения. Ключ представляет собой объект, с помощью которого значение извлекается впоследствии. Следовательно, в коллекции, реализующей интерфейс IDictionary, хранятся пары "ключ-значение". Как только подобная пара будет сохранена, ее мож но извлечь с помощью ключа. Интерфейс IDictionary наследует от интерфейсов ICollection и IEnumerable. Методы, объявленные в интерфейсе IDictionary, све дены в табл. 25.3. Некоторые из них генерируют исключение ArgumentNullException при попытке указать пустой ключ, поскольку пустые ключи не допускаются. Таблица 25.3. Методы, определенные в интерфейсе IDictionary Для добавления пары "ключ-значение" в коллекцию типа IDictionary служит метод Add(). Обратите внимание на то, что ключ и его значение указываются отдель но. А для удаления элемента из коллекции следует указать ключ этого объекта при вызове метода Remove(). И для опорожнения коллекции вызывается метод Clear(). Для того чтобы выяснить, содержит ли коллекция конкретный объект, вызыва ется метод Contains() с указанным ключом искомого элемента. С помощью мето да GetEnumerator() получается перечислитель, совместимый с коллекцией типа IDictionary. Этот перечислитель оперирует парами "ключ-значение". В интерфейсе IDictionary определяются перечисленные ниже свойства. Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступны в отдельных списках с помощью свойств Keys и Values. Кроме того, в интерфейсе IDictionary определяется следующий индексатор. object this[object key] { get; set; } Метод Описание void Add(object key, object value) void Clear() Добавляет в вызывающую коллекцию пару "ключ– значение”, определяемую параметрами key и value Удаляет все пары "ключ-значение" из вызывающей коллекции bool Contains(object key) Возвращает логическое значение true, если вызываю щая коллекция содержит объект key в качестве ключа, в противном случае – логическое значение false IDictionaryEnumerator GetEnumerator() Возвращает перечислитель для вызывающей коллек ции void Remove(object key) Удаляет из коллекции элемент, ключ которого равен зна чению параметра key Свойство Назначение bool IsFixedSize { get; } Принимает логическое значение true, если словарь имеет фиксированный размер bool IsReadOnly { get; } Принимает логическое значение true, если словарь до ступен только для чтения ICollection Keys { get; } Получает коллекцию ключей ICollection Values { get; } Получает коллекцию значений Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в качестве индекса в данном случае служит ключ элемента, а не собственно индекс. Интерфейсы IEnumerable, IEnumerator и IDictionaryEnumerator Интерфейс IEnumerable является необобщенным, и поэтому он должен быть реализован в классе для поддержки перечислителей. Как пояснялось выше, интер фейс IEnumerable реализуется во всех классах необобщенных коллекций, посколь ку он наследуется интерфейсом ICollection. Ниже приведен единственный метод GetEnumerator(), определяемый в интерфейсе IEnumerable. IEnumerator GetEnumerator() Он возвращает коллекцию. Благодаря реализации интерфейса IEnumerable мож но также получать содержимое коллекции в цикле foreach. В интерфейсе IEnumerator определяются функции перечислителя. С помощью ме тодов этого интерфейса можно циклически обращаться к содержимому коллекции. Если в коллекции содержатся пары "ключ-значение" (словари), то метод GetEnumerator() возвращает объект типа IDictionaryEnumerator, а не типа IEnumerator. Интерфейс IDictionaryEnumerator наследует от интерфейса IEnumerator и вводит дополни тельные функции, упрощающие перечисление словарей. В интерфейсе IEnumerator определяются также методы MoveNext() и Reset() и свойство Current. Способы их применения подробнее описываются далее в этой главе. А до тех пор следует отметить, что свойство Current содержит элемент, полу чаемый в текущий момент. Метод MoveNext() осуществляет переход к следующему элементу коллекции, а метод Reset() возобновляет перечисление с самого начала. Интерфейсы IComparer и IEqualityComparer В интерфейсе IComparer определяется метод Compare() для сравнения двух объектов. int Compare(object х, object у) Он возвращает положительное значение, если значение объекта х больше, чем у объекта у; отрицательное – если значение объекта х меньше, чем у объекта у; и ну левое – если сравниваемые значения равны. Данный интерфейс можно использовать для указания способа сортировки элементов коллекции. В интерфейсе IEqualityComparer определяются два метода. bool Equals(object х, object у) int GetHashCode(object obj) Метод Equals() возвращает логическое значение true, если значения объектов х и у равны. А метод GetHashCode() возвращает хеш-код для объекта obj. Интерфейсы IStructuralComparable и IStructuralEquatable Оба интерфейса IStructuralComparable и IStructuralEquatable добавлены в версию 4.0 среды .NET Framework. В интерфейсе IStructuralComparable определя ется метод CompareTo(), который задает способ структурного сравнения двух объектов для целей сортировки. (Иными словами, Метод CompareTo() сравнивает содержимое объектов, а не ссылки на них.) Ниже приведена форма объявления данного метода. int CompareTo(object other, IComparer comparer) Он должен возвращать -1, если вызывающий объект предшествует другому объекту other; 1, если вызывающий объект следует после объекта other; и наконец, 0, если значения обоих объектов одинаковы для целей сортировки. А само сравнение обеспе чивает объект, передаваемый через параметр comparer. Интерфейс IStructuralEquatable служит для выяснения структурного равен ства путем сравнения содержимого двух объектов. В этом интерфейсе определены сле дующие методы. bool Equals(object other, IEqualityComparer comparer) int GetHashCode(IEqualityComparer comparer) Метод Equals() должен возвращать логическое значение true, если вызывающий объект и другой объект other равны. А метод GetHashCode() должен возвращать хеш-код для вызывающего объекта. Результаты, возвращаемые обоими методами, должны быть совместимы. Само сравнение обеспечивает объект, передаваемый через параметр comparer. Структура DictionaryEntry В пространстве имен System.Collections определена структура DictionaryEntry. Необобщенные коллекции пар "ключ-значение" сохраняют эти пары в объекте типа DictionaryEntry. В данной структуре определяются два следующих свойства. public object Key { get; set; } public object Value { get; set; } Эти свойства служат для доступа к ключу или значению, связанному с элементом коллекции. Объект типа DictionaryEntry может быть сконструирован с помощью конструктора: public DictionaryEntry(object key, object value) где key обозначает ключ, a value – значение. Классы необобщенных коллекций А теперь, когда представлены интерфейсы необобщенных коллекций, можно пе рейти к рассмотрению стандартных классов, в которых они реализуются. Ниже при ведены классы необобщенных коллекций, за исключением коллекции типа BitArray, рассматриваемой далее в этой главе. Класс Описание ArrayList Определяет динамический массив, т.е. такой массив, который может при необходимости увеличивать свой размер Hashtable Определяет хеш-таблицу для пар “ключ-значение” Queue Определяет очередь, или список, действующий по принципу “первым при шел – первым обслужен” SortedList Определяет отсортированный список пар “ключ-значение” Stack Определяет стек, или список, действующий по принципу "первым пришел – последним обслужен” Каждый из этих классов коллекций подробно рассматривается и демонстрируется далее на конкретных примерах. Класс ArrayList В классе ArrayList поддерживаются динамические массивы, расширяющиеся и сокращающиеся по мере необходимости. В языке C# стандартные массивы имеют фик сированную длину, которая не может изменяться во время выполнения программы. Это означает, что количество элементов в массиве нужно знать заранее. Но иногда тре буемая конкретная длина массива остается неизвестной до самого момента выполне ния программы. Именно для таких ситуаций и предназначен класс ArrayList. В клас се ArrayList определяется массив переменной длины, который состоит из ссылок на объекты и может динамически увеличивать и уменьшать свой размер. Массив типа ArrayList создается с первоначальным размером. Если этот размер превышается, то массив автоматически расширяется. А при удалении объектов из такого массива он автоматически сокращается. Коллекции класса ArrayList широко применяются в практике программирования на С#. Именно поэтому они рассматриваются здесь под робно. Но многие способы применения коллекций класса ArrayList распространя ются и на другие коллекции, в том числе и на обобщенные. В классе ArrayList реализуются интерфейсы ICollection, IList, IEnumerable и ICloneable. Ниже приведены конструкторы класса ArrayList. public ArrayList() public ArrayList(ICollection c) public ArrayList(int capacity) Первый конструктор создает пустую коллекцию класса ArrayList с нулевой перво начальной емкостью. Второй конструктор создает коллекцию типа ArrayList с коли чеством инициализируемых элементов, которое определяется параметром с и равно первоначальной емкости массива. Третий конструктор создает коллекцию, имеющую указанную первоначальную емкость, определяемую параметром сараcity. В данном случае емкость обозначает размер базового массива, используемого для хранения эле ментов коллекции. Емкость коллекции типа ArrayList может увеличиваться автома тически по мере добавления в нее элементов. В классе ArrayList определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Некоторые из наиболее часто используемых методов класса ArrayList перечислены в табл. 25.4. Коллекцию класса ArrayList можно отсортировать, вызвав метод Sort(). В этом случае поиск в отсо ртированной коллекции с помощью метода BinarySearch() становится еще более эффективным. Содержимое коллекции типа ArrayList можно также обратить, вы звав метод Reverse(). Таблица 25.4. Наиболее часто используемые методы, определенные в классе ArrayList Метод Описание public virtual void AddRange(Icollection с) public virtual int BinarySearch(object value) Добавляет элементы из коллекции с в конец вызываю щей коллекции типа ArrayList Выполняет поиск в вызывающей коллекции значения value. Возвращает индекс найденного элемента. Если искомое значение не найдено, возвращает отрицатель ное значение. Вызывающий список должен быть отсо ртирован Продолжение табл. 25.4 Метод Описание public virtual int BinarySearch(object value, Icomparer comparer) Выполняет поиск в вызывающей коллекции значения value, используя для сравнения способ, определяемый параметром comparer. Возвращает индекс совпавше го элемента. Если искомое значение не найдено, воз вращает отрицательное значение. Вызывающий список должен быть отсортирован public virtual int BinarySearch(int index, int count, object value, IComparer comparer) Выполняет поиск в вызывающей коллекции значения value, используя для сравнения способ, определяемый параметром comparer. Поиск начинается с элемента, указываемого по индексу index, и включает количество элементов, определяемых параметром count. Метод воз вращает индекс совпавшего элемента. Если искомое зна чение не найдено, метод возвращает отрицательное зна чение. Вызывающий список должен быть отсортирован public virtual void СоруТо(Array array) Копирует содержимое вызывающей коллекции в мас сив array, который должен быть одномерным и совме стимым по типу с элементами коллекции public virtual void СоруТо(Array array, int arrayIndex) Копирует содержимое вызывающей коллекции в массив array, начиная с элемента, указываемого по индексу arrayIndex. Целевой массив должен быть одномер ным и совместимым по типу с элементами коллекции public virtual void CopyTo(int index, Array array, int arrayIndex, int count) Копирует часть вызывающей коллекции, начиная с эле мента, указываемого по индексу index, и включая ко личество элементов, определяемых параметром count, в массив array, начиная с элемента, указываемого по индексу arrayIndex. Целевой массив должен быть одно мерным и совместимым по типу с элементами коллекции public static ArrayList FixedSize(ArrayList list) public virtual ArrayList GetRange(int index, int count) Заключает коллекцию list в оболочку типа ArrayList с фиксированным размером и возвращает результат Возвращает часть вызывающей коллекции типа ArrayList. Часть возвращаемой коллекции начинает ся с элемента, указываемого по индексу index, и вклю чает количество элементов, определяемое параметром count. Возвращаемый объект ссылается на те же эле менты, что и вызывающий объект public virtual int IndexOf(object value) Возвращает индекс первого вхождения объекта value в вызывающей коллекции. Если искомый объект не об наружен, возвращает значение -1 public virtual void InsertRange(int index, ICollection c) Вставляет элементы коллекции с в вызывающую кол лекцию, начиная с элемента, указываемого по индексу index public virtual int LastlndexOf(object value) Возвращает индекс последнего вхождения объекта value в вызывающей коллекции. Если искомый объект не обнаружен, метод возвращает значение -1 Окончание табл. 25.4 В классе ArrayList поддерживается также ряд методов, оперирующих элемента ми коллекции в заданных пределах. Так, в одну коллекцию типа ArrayList можно вставить другую коллекцию, вызвав метод InsertRange(). Для удаления из коллек ции элементов в заданных пределах достаточно вызвать метод RemoveRange(). А для Метод Описание public static ArrayList Readonly(ArrayList list) Заключает коллекцию list в оболочку типа ArrayList, доступную только для чтения, и возвра щает результат public virtual void RemoveRange(int index, int count) Удаляет часть вызывающей коллекции, начиная с эле мента, указываемого по индексу index, и включая количество элементов, определяемое параметром count public virtual void Reverse() Располагает элементы вызывающей коллекции в обрат ном порядке public virtual void Reverse(int index, int count) Располагает в обратном порядке часть вызывающей коллекции, начиная с элемента, указываемого по индек су index, и включая количество элементов, определяе мое параметром count public virtual void SetRange(int index, ICollection c) Заменяет часть вызывающей коллекции, начиная с эле мента, указываемого по индексу index, элементами коллекции с public virtual void Sort() Сортирует вызывающую коллекцию по нарастающей public virtual void Sort(Icomparer comparer) Сортирует вызывающую коллекцию, используя для срав нения способ, определяемый параметром comparer. Если параметр comparer имеет пустое значение, то для сравнения используется способ, выбираемый по умолчанию public virtual void Sort(int index, int count, Icomparer comparer) Сортирует вызывающую коллекцию, используя для срав нения способ, определяемый параметром comparer. Сортировка начинается с элемента, указываемого по индексу index, и включает количество элементов, определяемых параметром count. Если параметр comparer имеет пустое значение, то для сравнения ис пользуется способ, выбираемый по умолчанию public static ArrayList Synchronized(ArrayList list) Возвращает синхронизированный вариант коллекции типа ArrayList, передаваемой в качестве параметра list public virtual object[] ToArray() Возвращает массив, содержащий копии элементов вы зывающего объекта public virtual Array ToArray(Type type) Возвращает массив, содержащий копии элементов вы зывающего объекта. Тип элементов этого массива опре деляется параметром type public virtual void TrimToSize() Устанавливает значение свойства Capacity равным значению свойства Count перезаписи элементов коллекции типа ArrayList в заданных пределах элементами из другой коллекции служит метод SetRange(). И наконец, элементы коллекции можно сортировать или искать в заданных пределах, а не во всей коллекции. По умолчанию коллекция типа ArrayList не синхронизирована. Для получения синхронизированной оболочки, в которую заключается коллекция, вызывается метод Synchronized(). В классе ArrayList имеется также приведенное ниже свойство Capacity, помимо свойств, определенных в интерфейсах, которые в нем реализуются. public virtual int Capacity { get; set; } Свойство Capacity позволяет получать и устанавливать емкость вызывающей кол лекции типа ArrayList. Емкость обозначает количество элементов, которые может содержать коллекция типа ArrayList до ее вынужденного расширения. Как упоми налось выше, коллекция типа ArrayList расширяется автоматически, и поэтому за давать ее емкость вручную необязательно. Но из соображений эффективности это ино гда можно сделать, если количество элементов коллекции известно заранее. Благодаря этому исключаются издержки на выделение дополнительной памяти. С другой стороны, если требуется сократить размер базового массива коллекции типа ArrayList, то для этой цели достаточно установить меньшее значение свойства Capacity. Но это значение не должно быть меньше значения свойства Count. Напом ним, что свойство Count определено в интерфейсе ICollection и содержит количе ство объектов, хранящихся в коллекции на данный момент. Всякая попытка установить значение свойства Capacity меньше значения свойства Count приводит к генериро ванию исключения ArgumentOutOfRangeException. Поэтому для получения такого количества элементов коллекции типа ArrayList, которое содержится в ней на дан ный момент, следует установить значение свойства Capacity равным значению свой ства Count. Для этой цели можно также вызвать метод TrimToSize(). В приведенном ниже примере программы демонстрируется применение класса ArrayList. В ней сначала создается коллекция типа ArrayList, а затем в эту коллек цию вводятся символы, после чего содержимое коллекции отображается. Некоторые элементы затем удаляются из коллекции, и ее содержимое отображается вновь. После этого в коллекцию вводятся дополнительные элементы, что вынуждает увеличить ее емкость. И наконец, содержимое элементов коллекции изменяется. // Продемонстрировать применение класса ArrayList. using System; using System.Collections; class ArrayListDemo { static void Main() { // Создать коллекцию в виде динамического массива. ArrayList al = new ArrayList(); Console.WriteLine("Исходное количество элементов: " + al.Count); Console.WriteLine(); Console.WriteLine("Добавить 6 элементов"); // Добавить элементы в динамический массив. al.Add('С'); al.Add('A'); al.Add('E'); al.Add('В'); al.Add('D'); al.Add('F'); Console.WriteLine("Количество элементов: " + al.Count); // Отобразить содержимое динамического массива, // используя индексирование массива. Console.Write("Текущее содержимое: "); for(int i=0; i < al.Count; i++) Console.Write(al[i] + " "); Console.WriteLine("n"); Console.WriteLine("Удалить 2 элемента"); // Удалить элементы из динамического массива. al.Remove('F'); al.Remove('A'); Console.WriteLine("Количество элементов: " + al.Count); // Отобразить содержимое динамического массива, используя цикл foreach. Console.Write("Содержимое: "); foreach(char с in al) Console.Write(с + " "); Console.WriteLine("n"); Console.WriteLine("Добавить еще 20 элементов"); // Добавить количество элементов, достаточное для // принудительного расширения массива. for (int i=0; i < 20; i++) al.Add((char)('a' + i)); Console.WriteLine("Текущая емкость: " + al.Capacity); Console.WriteLine("Количество элементов после добавления 20 новых: " + al.Count); Console.Write("Содержимое: "); foreach(char с in al) Console.Write(с + " "); Console.WriteLine("n"); // Изменить содержимое динамического массива, // используя индексирование массива. Console.WriteLine("Изменить три первых элемента"); al[0] = 'X'; al[1] = 'Y'; al[2] = 'Z'; Console.Write("Содержимое: "); foreach(char с in al) Console.Write(c + " Console.WriteLine(); } } Вот к какому результату приводит выполнение этой программы. Исходное количество элементов: 0 Добавить 6 элементов Количество элементов: 6 Текущее содержимое: С А Е В D F Удалить 2 элемента Количество элементов: 4 Содержимое: С Е В D Добавить еще 20 элементов Текущая емкость: 32 Количество элементов после добавления 20 новых: 24 Содержимое: C E B 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 Сортировка и поиск в коллекции типа ArrayList Коллекцию типа ArrayList можно отсортировать с помощью метода Sort(). В этом случае поиск в отсортированной коллекции с помощью метода BinarySearch() становится еще более эффективным. Применение обоих методов демонстрируется в приведенном ниже примере программы. // Отсортировать коллекцию типа ArrayList и осуществить в ней поиск. using System; using System.Collections; class SortSearchDemo { static void Main() { // Создать коллекцию в виде динамического массива. ArrayList al = new ArrayList(); // Добавить элементы в динамический массив. al.Add(55); al.Add(43); al.Add(-4); al.Add(88); al.Add(3); al.Add(19); Console.Write("Исходное содержимое: "); foreach(int i in al) Console.Write(i + " "); Console.WriteLine("n"); // Отсортировать динамический массив. al.Sort(); // Отобразить содержимое динамического массива, используя цикл foreach. Console.Write("Содержимое после сортировки: "); foreach(int i in al) Console.Write(i + " "); Console.WriteLine("n"); Console.WriteLine("Индекс элемента 43: " + al.BinarySearch(43)); } } Ниже приведен результат выполнения этой программы. Исходное содержимое: 55 43 -4 88 3 19 Содержимое после сортировки: -4 3 19 43 55 88 Индекс элемента 43: 3 В одной и той же коллекции типа ArrayList могут храниться объекты любого типа. Тем не менее во время сортировки и поиска в ней эти объекты приходится срав нивать. Так, если бы список объектов в приведенном выше примере программы со держал символьную строку, то их сравнение привело бы к исключительной ситуации. Впрочем, для сравнения символьных строк и целых чисел можно создать специальные методы. О таких методах сравнения речь пойдет далее в этой главе. Получение массива из коллекции типа ArrayList В работе с коллекцией типа ArrayList иногда требуется получить из ее содер жимого обычный массив. Этой цели служит метод ТоАrrау(). Для преобразования коллекции в массив имеется несколько причин. Две из них таковы: потребность в уско рении обработки при выполнении некоторых операций и необходимость передавать массив методу, который не перегружается, чтобы принять коллекцию. Но независимо от конкретной причины коллекция типа ArrayList преобразуется в обычный массив довольно просто, как показано в приведенном ниже примере программы. // Преобразовать коллекцию типа ArrayList в обычный массив. using System; using System.Collections; class ArrayListToArray { static void Main() { ArrayList al = new ArrayList(); // Добавить элементы в динамический массив. al.Add(1); al.Add(2); al.Add(3); al.Add(4); Console.Write("Содержимое: "); foreach(int i in al) Console.Write(i + " "); Console.WriteLine(); // Получить массив. int[] ia = (int[]) al.ToArray(typeof(int)); int sum = 0; // Просуммировать элементы массива. for(int i=0; i "); int a = (int) st.Pop(); Метод Описание public virtual void Clear() Устанавливает свойство Count равным нулю, очи щая, по существу, стек public virtual bool Contains (object obj) Возвращает логическое значение true, если объект obj содержится в вызывающем стеке, а иначе – логическое значение false public virtual object Peek() Возвращает элемент, находящийся на вершине сте ка, но не удаляет его public virtual object Pop() Возвращает элемент, находящийся на вершине сте ка, удаляя его по ходу дела public virtual void Push (object obj) Помещает объект obj в стек public static Stack Synchronized(Stack stack) Возвращает синхронизированный вариант коллек ции типа Stack, передаваемой в качестве параме тра stack public virtual object[] ToArray() Возвращает массив, содержащий копии элементов вызывающего стека Console.WriteLine(a); Console.Write("Содержимое стека: "); foreach(int i in st) Console.Write(i + " "); Console.WriteLine(); } static void Main() { Stack st = new Stack (); foreach(int i in st) Console.Write(i + " "); Console.WriteLine(); ShowPush(st, 22); ShowPush(st, 65); ShowPush(st, 91); ShowPop(st); ShowPop(st); ShowPop(st); try { ShowPop(st); } catch (InvalidOperationException) { Console.WriteLine("Стек пуст."); } } } Ниже приведен результат выполнения этой программы. Обратите внимание на то, как обрабатывается исключение InvalidOperationException, генерируемое при попытке извлечь элемент из пустого стека. Поместить в стек: Push (22) Содержимое стека: 22 Поместить в стек: Push(65) Содержимое стека: 65 22 Поместить в стек: Push (91) Содержимое стека: 91 65 22 Извлечь из стека: Pop -> 91 Содержимое стека: 65 22 Извлечь из стека: Pop -> 65 Содержимое стека: 22 Извлечь из стека: Pop -> 22 Содержимое стека: Извлечь из стека: Pop -> Стек пуст. Класс Queue Еще одной распространенной структурой данных является очередь, действующая по принципу: первым пришел – первым обслужен. Это означает, что первым из оче реди извлекается элемент, помещенный в нее первым. Очереди часто встречаются в реальной жизни. Многим из нас нередко приходилось стоять в очередях к кассе в бан ке, магазине или столовой. В программировании очереди применяются для хранения таких элементов, как процессы, выполняющиеся в данный момент в системе, списки приостановленных транзакций в базе данных или пакеты данных, полученные по Ин тернету. Кроме того, очереди нередко применяются в области имитационного моде лирования. Класс коллекции, поддерживающий очередь, носит название Queue. В нем реали зуются интерфейсы ICollection, IEnumerable и ICloneable. Этот класс создает динамическую коллекцию, которая расширяется, если в ней необходимо хранить вво димые элементы. Так, если в очереди требуется свободное место, ее размер увеличива ется на коэффициент роста, который по умолчанию равен 2,0. В классе Queue определяются приведенные ниже конструкторы. public Queue() public Queue (int capacity) public Queue (int capacity, float growFactor) public Queue (ICollection col) В первой форме конструктора создается пустая очередь с выбираемыми по умол чанию емкостью и коэффициентом роста 2,0. Во второй форме создается пустая оче редь, первоначальный размер которой определяет емкость, задаваемая параметром сараcity, а коэффициент роста по умолчанию выбирается для нее равным 2,0. В тре тьей форме допускается указывать не только емкость (в качестве параметра capacity), но и коэффициент роста создаваемой очереди (в качестве параметра growFactor в пределах от 1,0 до 10,0). И в четвертой форме создается очередь, состоящая из элемен тов указываемой коллекции col. Ее первоначальная емкость равна количеству указан ных элементов, а коэффициент роста по умолчанию выбирается для нее равным 2,0. В классе Queue определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Некоторые из наиболее ча сто используемых методов этого класса перечислены в табл. 25.8. Эти методы обыч но применяются следующим образом. Для того чтобы поместить объект в очередь, вызывается метод Enqueue(). Если требуется извлечь и удалить первый объект из начала очереди, то вызывается метод Dequeue(). Если же требуется извлечь, но не удалять следующий объект из очереди, то вызывается метод Рееk(). А если методы Dequeue() и Рееk() вызываются, когда очередь пуста, то генерируется исключение InvalidOperationException. Таблица 25.8. Наиболее часто используемые методы, определенные в классе Queue Метод Описание public virtual void Clear() Устанавливает свойство Count равным нулю, очи щая, по существу, очередь public virtual bool Contains(object obj) Возвращает логическое значение true, если объ ект obj содержится в вызывающей очереди, а ина че – логическое значение false public virtual object Dequeue() public virtual void Enqueue(object obj) Возвращает объект из начала вызывающей очере ди. Возвращаемый объект удаляется из очереди Добавляет объект obj в конец очереди Окончание табл. 25.8 Метод Описание public virtual object Peek() Возвращает объект из начала вызывающей очере ди, но не удаляет его public static Queue Synchronized(Queue queue) Возвращает синхронизированный вариант коллек ции типа Queue, передаваемой в качестве параме тра queue public virtual object[] ToArray() Возвращает массив, который содержит копии эле ментов из вызывающей очереди public virtual void TrimToSize() Устанавливает значение свойства Capacity рав ным значению свойства Count В приведенном ниже примере программы демонстрируется применение класса Queue. // Продемонстрировать применение класса Queue. using System; using System.Collections; class QueueDemo { static void ShowEnq(Queue q, int a) { q.Enqueue(a); Console.WriteLine("Поместить в очередь: Enqueue(" + a + ")"); Console.Write("Содержимое очереди: "); foreach(int i in q) Console.Write(i + " "); Console.WriteLine (); } static void ShowDeq(Queue q) { Console.Write("Извлечь из очереди: Dequeue -> "); int a = (int) q.Dequeue(); Console.WriteLine(a); Console.Write("Содержимое очереди: "); foreach(int i in q) Console.Write(i + " "); Console.WriteLine(); } static void Main() { Queue q = new Queue(); foreach(int i in q) Console.Write(i + " "); Console.WriteLine(); ShowEnq(q, 22); ShowEnq(q, 65); ShowEnq(q, 91); ShowDeq(q); ShowDeq(q); ShowDeq(q); try { ShowDeq (q); } catch (InvalidOperationException) { Console.WriteLine("Очередь пуста."); } } } Эта программа дает следующий результат. Поместить в очередь: Enqueue(22) Содержимое очереди: 22 Поместить в очередь: Enqueue(65) Содержимое очереди: 22 65 Поместить в очередь: Enqueue(91) Содержимое очереди: 22 65 91 Извлечь из очереди: Dequeue -> 22 Содержимое очереди: 65 91 Извлечь из очереди: Dequeue -> 65 Содержимое очереди: 91 Извлечь из очереди: Dequeue -> 91 Содержимое очереди: Извлечь из очереди: Dequeue -> Очередь пуста. Хранение отдельных битов в классе коллекции BitArray Класс BitArray служит для хранения отдельных битов в коллекции. А поскольку в коллекции этого класса хранятся биты, а не объекты, то своими возможностями он отличается от классов других коллекций. Тем не менее в классе BitArray реализуют ся интерфейсы ICollection и IEnumerable как основополагающие элементы под держки всех типов коллекций. Кроме того, в классе BitArray реализуется интерфейс ICloneable. В классе BitArray определено несколько конструкторов. Так, с помощью приве денного ниже конструктора можно сконструировать объект типа BitArray из массива логических значений. public BitArray(bool[] values) В данном случае каждый элемент массива values становится отдельным битом в коллекции. Это означает, что каждому элементу массива values соответствует отдель ный бит в коллекции. Более того, порядок расположения элементов в массиве values сохраняется и в коллекции соответствующих им битов. Коллекцию типа BitArray можно также составить из массива байтов, используя следующий конструктор. public BitArray(byte[] bytes) Здесь битами в коллекции становится уже целый их набор из массива bytes, при чем элемент bytes[0] обозначает первые 8 битов, элемент bytes[1] – вторые 8 би тов и т.д. Аналогично, коллекцию типа BitArray можно составить из массива цело численных значений, используя приведенный ниже конструктор. public BitArray(int[ ] values) В данном случае элемент values[0] обозначает первые 32 бита, элемент values[1] – вторые 32 бита и т.д. С помощью следующего конструктора можно составить коллекцию типа BitArray, указав ее конкретный размер: public BitArray(int length) где length обозначает количество битов в коллекции, которые инициализируются логическим значением false. В приведенном ниже конструкторе можно указать не только размер коллекции, но и первоначальное значение составляющих ее битов. public BitArray(int length, bool defaultValue) В данном случае все биты в коллекции инициализируются значением defaultValue, передаваемым конструктору в качестве параметра. И наконец, новую коллекцию типа BitArray можно создать из уже существую щей, используя следующий конструктор. public BitArray(BitArray bits) Вновь сконструированный объект будет содержать такое же количество битов, как и в указываемой коллекции bits, а в остальном это будут две совершенно разные кол лекции. Коллекции типа BitArray подлежат индексированию. По каждому индексу указы вается отдельный бит в коллекции, причем нулевой индекс обозначает младший бит. В классе BitArray определяется ряд собственных методов, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Методы этого класса приведе ны в табл. 25.9. Обратите внимание на то, что в классе BitArray не поддерживается метод Synchronized(). Это означает, что для коллекций данного класса синхронизи рованная оболочка недоступна, а свойство IsSynchronized всегда имеет логическое значение false. Тем не менее для управления доступом к коллекции типа BitArray ее можно синхронизировать для объекта, предоставляемого упоминавшимся ранее свойством SyncRoot. Таблица 25.9. Методы, определенные в классе BitArray Метод Описание public BitArray And(BitArray value) Выполняет операцию логического умножения И би тов вызывающего объекта и коллекции value. Воз вращает коллекцию типа BitArray, содержащую результат public bool Get(int index) Возвращает значение бита, указываемого по ин дексу index public BitArray Not() Выполняет операцию поразрядного логического отри цания НЕ битов вызывающей коллекции и возвраща ет коллекцию типа BitArray, содержащую результат Окончание табл. 25.9 В классе BitArray определяется также собственное свойство, помимо тех, что ука заны в интерфейсах, которые в нем реализуются. public int Length { get; set; } Свойство Length позволяет установить или получить количество битов в коллек ции. Следовательно, оно возвращает такое же значение, как и стандартное свойство Count, определяемое для всех коллекций. В отличие от свойства Count, свойство Length доступно не только для чтения, но и для записи, а значит, с его помощью мож но изменить размер коллекции типа BitArray. Так, при сокращении коллекции типа BitArray лишние биты усекаются, начиная со старшего разряда. А при расширении коллекции типа BitArray дополнительные биты, имеющие логическое значение false, вводятся в коллекцию, начиная с того же старшего разряда. Кроме того, в классе BitArray определяется следующий индексатор. public bool this[int index] { get; set; } С помощью этого индексатора можно получать или устанавливать значение элемента. В приведенном ниже примере демонстрируется применение класса BitArray. // Продемонстрировать применение класса BitArray. using System; using System.Collections; class BADemo { public static void ShowBits(string rem, BitArray bits) { Console.WriteLine(rem); for(int i=0; i < bits.Count; i++) Console.Write("{0, -6} ", bits[i]); Console.WriteLine("n"); } static void Main() { BitArray ba = new BitArray(8); byte[] b = { 67 }; BitArray ba2 = new BitArray(b); Метод Описание public BitArray Or(BitArray value) Выполняет операцию логического сложения ИЛИ би тов вызывающего объекта и коллекции value. Воз вращает коллекцию типа BitArray, содержащую результат public void Set(int index, bool value) Устанавливает бит, указываемый по индексу Index, равным значению value public void SetAll(bool value) Устанавливает все биты равными значению value public BitArray Xor(BitArray value) Выполняет логическую операцию исключающее ИЛИ над битами вызывающего объекта и коллекции value. Возвращает коллекцию типа BitArray, со держащую результат ShowBits("Исходное содержимое коллекции ba:", bа); ba = ba.Not(); ShowBits("Содержимое коллекции bа после логической операции NOT:", ba); ShowBits("Содержимое коллекции bа2:", bа2); BitArray bа3 = bа.Хоr(bа2); ShowBits("Результат логической операции ba XOR bа2:", bаЗ); } } Эта программа дает следующий результат. Исходное содержимое коллекции Ьа: False False False False False False False False Содержимое коллекции ba после логической операции NOT: True True True True True True True True Содержимое коллекции bа2: True True False False False False ffrue False Результат логической операции ba XOR ba2: False False True True True True False True Специальные коллекции В среде .NET Framework предусмотрен ряд специальных коллекций, оптимизиро ванных для работы с данными конкретного типа иди для их обработки особым обра зом. Классы этих необобщенных коллекций определены в пространстве имен System. Collections.Specialized и перечислены ниже. Класс специальной коллекции Описание CollectionsUtil Содержит фабричные методы для создания коллекций HybridDictionary Предназначен для коллекций, в которых для хранения неболь шого количества пар “ключ-значение" используется класс ListDictionary. При превышении коллекцией определен ного размера автоматически используется класс Hashtable для хранения ее элементов ListDictionary Предназначен для коллекций, в которых для хранения пар “ключ– значение” используется связный список. Такие коллекции реко мендуются только для хранения небольшого количества элементов NameValueCollection Предназначен для отсортированных коллекций, в которых хра нятся пары “ключ-значение”, причем и ключ, и значение от носятся к типу string OrderedDictionary Предназначен для коллекций, в которых хранятся индексируе мые пары “ключ-значение” StringCollection Предназначен для коллекций, оптимизированных для хранения символьных строк StringDictionary Предназначен для хеш-таблиц, в которых хранятся пары “ключ-значение”, причем и ключ, и значение относятся к типу string Кроме того, в пространстве имен System.Collections определены три базовых абстрактных класса: CollectionBase, ReadOnlyCollectionBase и DictionaryBase. Эти классы могут наследоваться и служить в качестве отправной точки для разработки собственных специальных коллекций. Обобщенные коллекции Благодаря внедрению обобщений прикладной интерфейс Collections API значи тельно расширился, в результате чего количество классов коллекций и интерфей сов удвоилось. Обобщенные коллекции объявляются в пространстве имен System. Collections.Generic. Как правило, классы обобщенных коллекций являются не бо лее чем обобщенными эквивалентами рассматривавшихся ранее классов необобщен ных коллекций, хотя это соответствие не является взаимно однозначным. Например, в классе обобщенной коллекции LinkedList реализуется двунаправленный список, тогда как в необобщенном эквиваленте его не существует. В некоторых случаях одни и те же функции существуют параллельно в классах обобщенных и необобщенных кол лекций, хотя и под разными именами. Так, обобщенный вариант класса ArrayList называется List, а обобщенный вариант класса HashTable – Dictionary. Кроме того, конкретное содержимое различных интерфейсов и классов реорганизуется с ми нимальными изменениями для переноса некоторых функций из одного интерфейса в другой. Но в целом, имея ясное представление о необобщенных коллекциях, можно без особого труда научиться применять и обобщенные коллекции. Как правило, обобщенные коллекции действуют по тому же принципу, что и не обобщенные, за исключением того, что обобщенные коллекции типизированы. Это означает, что в обобщенной коллекции можно хранить только те элементы, которые совместимы по типу с ее аргументом. Так, если требуется коллекция для хранения не связанных друг с другом разнотипных данных, то для этой цели следует использовать классы необобщенных коллекций. А во всех остальных случаях, когда в коллекции должны храниться объекты только одного типа, выбор рекомендуется останавливать на классах обобщенных коллекций. Обобщенные коллекции определяются в ряде интерфейсов и классов, реализую щих эти интерфейсы. Все они описываются далее по порядку. Интерфейсы обобщенных коллекций В пространстве имен System.Collections.Generic определен целый ряд интер фейсов обобщенных коллекций, имеющих соответствующие аналоги среди интерфей сов необобщенных коллекций. Все эти интерфейсы сведены в табл. 25.10. Таблица 25.10. Интерфейсы обобщенных коллекций Интерфейс Описание ICollection Определяет основополагающие свойства обобщенных коллекций IComparer Определяет обобщенный метод Compare() для срав нения объектов, хранящихся в коллекции IDictionary Определяет обобщенную коллекцию, состоящую из пар "ключ-значение’’ Окончание табл. 25.10 Интерфейс Описание IEnumerable Определяет обобщенный метод GetEnumerator(), предоставляющий перечислитель для любого класса коллекции Enumerator Предоставляет методы, позволяющие получать содержи мое коллекции по очереди IEqualityComparer Сравнивает два объекта на предмет равенства IList Определяет обобщенную коллекцию, доступ к которой можно получить с помощью индексатора Интерфейс ICollection В интерфейсе ICollection определен ряд свойств, которые являются общими для всех обобщенных коллекций. Интерфейс ICollection является обобщенным вариантом необобщенного интерфейса ICollection, хотя между ними имеются не которые отличия. Итак, в интерфейсе ICollection определены следующие свойства. int Count { get; } bool IsReadOnly { get; } Свойство Count содержит ряд элементов, хранящихся в данный момент в коллек ции. А свойство IsReadOnly имеет логическое значение true, если коллекция доступ на только для чтения. Если же коллекция доступна как для чтения, так и для записи, то данное свойство имеет логическое значение false. Кроме того, в интерфейсе ICollection определены перечисленные ниже ме тоды. Обратите внимание на то, что в этом обобщенном интерфейсе определено не сколько большее количество методов, чем в его необобщенном аналоге. Некоторые из перечисленных выше методов генерируют исключе ние NotSupportedException, если коллекция доступна только для чтения. Метод Описание void Add(T item) Добавляет элемент item в вызывающую коллекцию. Гене рирует исключение NotSupportedException, если кол лекция доступна только для чтения void Clear() Удаляет все элементы из вызывающей коллекции bool Contains(T item) Возвращает логическое значение true, если вызывающая коллекция содержит элемент item, а иначе – логическое значение false void CopyTo(T[] array, int arrayIndex) Копирует содержимое вызывающей коллекции в массив array, начиная с элемента, указываемого по индексу arrayIndex void Remove(T item) Удаляет первое вхождение элемента item в вызывающей коллекции. Возвращает логическое значение true, если элемент item удален. А если этот элемент не найден в вы зывающей коллекции, то возвращается логическое значе ние false А поскольку интерфейс ICollection наследует от интерфейсов IEnumerable и IEnumerable, то он включает в себя также обобщенную и необобщенную формы метода GetEnumerator(). Благодаря тому что в интерфейсе ICollection реализуется интерфейс IEnumerable, в нем поддерживаются также методы расширения, определенные в классе Enumerable. Несмотря на то что методы расширения предназначены главным образом для поддержки LINQ, им можно найти и другое применение, в том числе и в коллекциях. Интерфейс IList В интерфейсе IList определяется такое поведение обобщенной коллекции, которое позволяет осуществлять доступ к ее элементам по индексу с отсчетом от нуля. Этот интерфейс наследует от интерфейсов IEnumerable, IEnumerable и ICollection и поэтому является обобщенным вариантом необобщенного интер фейса IList. Методы, определенные в интерфейсе IList, перечислены в табл. 25.11. В двух из этих методов предусматривается модификация коллекции. Если же коллекция доступна только для чтения или имеет фиксированный размер, то методы Insert() и RemoveAt() генерируют исключение NotSupportedException. Таблица 25.11. Методы, определенные в интерфейсе IList Метод Описание int IndexOf(Т item) Возвращает индекс первого вхождения элемента item в вызывающей коллекции. Если элемент item не обнару жен, то метод возвращает значение -1 void Insert(int index, T item) Вставляет в вызывающую коллекцию элемент item по индексу index void RemoveAtfint index) Удаляет из вызывающей коллекции элемент, расположен ный по указанному индексу index Кроме того, в интерфейсе IList определяется индексатор Т this[int index] { get; set; } который устанавливает или возвращает значение элемента коллекции по указанному индексу index. Интерфейс IDictionary В интерфейсе IDictionary определяется такое поведение обоб щенной коллекции, которое позволяет преобразовать уникальные ключи в соответ ствующие значения. Это означает, что в данном интерфейсе определяется коллекция, в которой хранятся пары "ключ-значение". Интерфейс IDictionary наследует от интерфейсов IEnumerable, IEnumerable> и ICollection> и поэтому является обобщенным вариантом необобщенного интерфейса IDictionary. Методы, объяв ленные в интерфейсе IDictionary, приведены в табл. 25.12. Все эти методы генерируют исключение ArgumentNullException при попытке указать пу стой ключ. Кроме того, в интерфейсе IDictionary определены перечислен ные ниже свойства. Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступны отдельными списками с помощью свойств Keys и Values. И наконец, в интерфейсе IDictionary определяется следующий индексатор. TValue this[TKey key] { get; set; } Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Следует, однако, иметь в виду, что в качестве индекса в данном случае служит ключ элемента, а не сам индекс. Интерфейсы IEnumerable и IEnumerator Интерфейсы IEnumerable и IEnumerator являются обобщенными эк вивалентами рассмотренных ранее необобщенных интерфейсов IEnumerable и IEnumerator. В них объявляются аналогичные методы и свойства, да и действуют они по тому же принципу. Разумеется, обобщенные интерфейсы оперируют данными только того типа, который указывается в аргументе типа. В интерфейсе IEnumerable метод GetEnumerator() объявляется следующим образом. IEnumerator GetEnumerator() Этот метод возвращает перечислитель типа Т для коллекции. А это означает, что он возвращает типизированный перечислитель. Таблица 25.12. Методы, определенные в интерфейсе IDictionary Метод Описание void Add(TKey key, TValue value) Добавляет в вызывающую коллекцию пару “ключ– значение”, определяемую параметрами key и value. Генерирует исключение ArgumentException, если ключ key уже находится в коллекции bool Contains(TKey key) Возвращает логическое значение true, если вызы вающая коллекция содержит элемент key в качестве ключа, а иначе – логическое значение false bool Remove(TKey key) Удаляет из коллекции элемент, ключ которого равен значению key bool TryGetValue(TKey key, out TValue value) Предпринимает попытку извлечь значение из коллек ции по указанному ключу key и присвоить это значе ние переменной value. При удачном исходе опера ции возвращается логическое значение true, а ина че – логическое значение false. Если ключ key не найден, переменной value присваивается значение, выбираемое по умолчанию Свойство Описание ICollection Keys { get; } Получает коллекцию ключей ICollection Values { get; } Получает коллекцию значений Кроме того, в интерфейсе IEnumerable определяются два таких же метода, как и в необобщенном его варианте: MoveNext() и Reset(). В этом интерфейсе объявля ется также обобщенный вариант свойства Current. Т Current { get; } Это свойство возвращает ссылку типа Т на следующий объект. А это означает, что обобщенный вариант свойства Current является типизированным. Но между интерфейсами IEnumerator и IEnumerator имеется одно важное различие: интерфейс IEnumerator наследует от интерфейса IDisposable, тогда как интерфейс IEnumerator не наследует от него. В интерфейсе IDisposable опреде ляется метод Dispose(), который служит для освобождения неуправляемых ресурсов. ПРИМЕЧАНИЕ В интерфейсе IEnumerable реализуется также необобщенный интерфейс IEnumerable. Это означает, что в нем поддерживается необобщенный вариант метода GetEnumerator(). Кроме того, в интерфейсе IEnumerable реализуется необобщен ный интерфейс IEnumerator, а следовательно, в нем поддерживаются необобщенные ва рианты свойства Current. Интерфейс IComparer Интерфейс IComparer является обобщенным вариантом рассмотренного ранее интерфейса IComparer. Главное отличие между ними заключается в том, что интер фейс IComparer обеспечивает типовую безопасность. В нем обобщенный вариант метода Compare() объявляется следующим образом. int Compare(Т х, Т у) В этом методе сравниваются объекты х и у. Он возвращает положительное значе ние, если значение объекта х больше, чем у объекта у; отрицательное – если значение объекта х меньше, чем у объекта у; и нулевое значение – если сравниваемые значения равны. Интерфейс IEqualityComparer Интерфейс IEqualityComparer полностью соответствует своему необобщен ному аналогу EqualityComparer. В нем определяются два следующих метода. bool Equals (Т х, Т у) int GetHashCode(Т obj) Метод Equals() должен возвратить логическое значение true, если значения объектов х и у равны. А метод GetHashCode() возвращает хеш-код для объекта obj. Если два сравниваемых объекта равны, то их хеш-коды также должны быть одинаковы. Интерфейс ISet Интерфейс ISet был добавлен в версию 4.0 среды .NET Framework. Он опре деляет поведение обобщенной коллекции, реализующей ряд уникальных элементов. Этот интерфейс наследует от интерфейсов IEnumerable, IEnumerable, а также ICollection. В интерфейсе ISet определен ряд методов, перечисленных в табл. 25.13. Обратите внимание на то, что параметры этих методов указываются как относящиеся к типу IEnumerable. Это означает, что в качестве второго аргумен та методу можно передать нечто, отличающееся от объектов типа ISet Но чаще всего оба аргумента оказываются экземплярами объектов типа ISet Таблица 25.13. Методы, определенные в интерфейсе ISet Метод Описание void ExceptWith(Ienumerable other) Удаляет из вызывающего множества те элементы, которые содержатся в другом множестве other void IntersectWith(IEnumerable other) После вызова этого метода вызывающее множе ство содержит пересечение своих элементов с эле ментами другого множества other bool IsProperSubsetOf(IEnumerable other) Возвращает логическое значение true, если вы зывающее множество является правильным под множеством другого множества other, а иначе – логическое значение false bool IsProperSupersetOf(IEnumera ble other) возвращает логическое значение true, если вы зывающее множество является правильным над множеством другого множества other, а иначе – логическое значение false bool IsSubsetOf(IEnumerable other) Возвращает логическое значение true, если вы зывающее множество является подмножеством другого множества other, а иначе – логическое значение false bool IsSupersetOf(IEnumerable other) Возвращает логическое значение true, если вы зывающее множество является надмножеством другого множества other, а иначе – логическое значение false bool Overlaps(IEnumerable other) Возвращает логическое значение true, если вы зывающее множество и другое множество other содержат хотя бы один общий элемент, а иначе – логическое значение false bool SetEquals(IEnumerable other) Возвращает логическое значение true, если все элементы вызывающего множества и другого мно жества other оказываются общими, а иначе – ло гическое значение false. Порядок расположения элементов не имеет значения, а дублирующиеся элементы во другом множестве other игнориру ются void SymmetricExceptWith (IEnumerable other) После вызова этого метода вызывающее множе ство будет содержать симметрическую разность своих элементов и элементов другого множества other void UnionWith(IEnumerable other) После вызова этого метода вызывающее множе ство будет содержать объединение своих элемен тов и элементов другого множества other Структура KeyValuePair В пространстве имен System.Collections.Generic определена структура KeyValuePair. Она служит для хранения ключа и его значения и применяется в классах обобщенных коллекций, в которых хранятся пары "ключ– значение", как, например, в классе Dictionary. В этой структуре определяются два следующих свойства. public TKey Key { get; }; public TValue Value { get; }; В этих свойствах хранятся ключ и значение соответствующего элемента коллекции. Для построения объекта типа KeyValuePair служит конструктор: public KeyValuePair(TKey key, TValue value) где key обозначает ключ, a value – значение. Классы обобщенных коллекций Как упоминалось ранее, классы обобщенных коллекций по большей части соот ветствуют своим необобщенным аналогам, хотя в некоторых случаях они носят другие имена. Отличаются они также своей организацией и функциональными возможно стями. Классы обобщенных коллекций определяются в пространстве имен System. Collections.Generic. В табл. 25.14 приведены классы, рассматриваемые в этой гла ве. Эти классы составляют основу обобщенных коллекций. Таблица 25.14. Основные классы обобщенных коллекций Класс Описание Dictionary Сохраняет пары “ключ-значение". Обеспечивает такие же функциональные возможности, как и необобщенный класс Hashtable HashSet Сохраняет ряд уникальных значений, используя хеш– таблицу LinkedList Сохраняет элементы в двунаправленном списке List Создает динамический массив. Обеспечивает такие же функциональные возможности, как и необобщенный класс ArrayList Queue Создает очередь. Обеспечивает такие же функциональ ные возможности, как и необобщенный класс Queue SortedDictionary Создает отсортированный список из пар “ключ– значение” SortedList Создает отсортированный список из пар "ключ– значение”. Обеспечивает такие же функциональные воз можности, как и необобщенный класс SortedList SortedSet Создает отсортированное множество Stack Создает стек. Обеспечивает такие же функциональные возможности, как и необобщенный класс Stack ПРИМЕЧАНИЕ В пространстве имен System.Collections.Generic находятся также следующие классы: класс SynchronizedCollection синхронизированной коллекции на осно ве класса IList; класс SynchronizedReadOnlyCollection, доступной толь ко для чтения синхронизированной коллекции на основе класса lList; абстрактный класс SynchronizedKeyCollection, служащий в качестве базового для клас са коллекции System.ServiceModel.UriSchemeKeyedCollection; а также класс KeyedByTypeCollection коллекции, в которой в качестве ключей используются от дельные типы данных. Класс List В классе List реализуется обобщенный динамический массив. Он ничем принципиально не отличается от класса необобщенной коллекции ArrayList. В этом классе реализуются интерфейсы ICollection, ICollection, IList, IList, IEnumerable и IEnumerable. У класса List имеются следующие конструкторы. public List() public List(IEnumerable collection) public List(int capacity) Первый конструктор создает пустую коллекцию класса List с выбираемой по умолчанию первоначальной емкостью. Второй конструктор создает коллекцию типа List с количеством инициализируемых элементов, которое определяется параметром collection и равно первоначальной емкости массива. Третий конструктор создает коллекцию типа List, имеющую первоначальную емкость, задаваемую параметром capacity. В данном случае емкость обозначает размер базового массива, используе мого для хранения элементов коллекции. Емкость коллекции, создаваемой в виде ди намического массива, может увеличиваться автоматически по мере добавления в нее элементов. В классе List определяется ряд собственных методов, помимо тех, что уже объ явлены в интерфейсах, которые в нем реализуются. Некоторые из наиболее часто ис пользуемых методов этого класса перечислены в табл. 25.15. Таблица 25.15. Наиболее часто используемые методы, определенные в классе List Метод Описание public virtual void AddRange(Icollection collection) Добавляет элементы из коллекции collection в конец вызывающей коллекции типа ArrayList public virtual int BinarySearch(T item) Выполняет поиск в вызывающей коллекции значе ния, задаваемого параметром item. Возвращает индекс совпавшего элемента. Если искомое зна чение не найдено, возвращается отрицательное значение. Вызывающий список должен быть отсо ртирован Продолжение табл. 25.15 Метод Описание public int BinarySearch(Т item, IComparer comparer) Выполняет поиск в вызывающей коллекции значе ния, задаваемого параметром item, используя для сравнения указанный способ, определяемый пара метром comparer. Возвращает индекс совпавшего элемента. Если искомое значение не найдено, воз вращается отрицательное значение. Вызывающий список должен быть отсортирован public int BinarySearch(int index, int count, T item, IComparer comparer) Выполняет поиск в вызывающей коллекции значе ния, задаваемого параметром item, используя для сравнения указанный способ, определяемый пара метром comparer. Поиск начинается с элемента, указываемого по индексу index, и включает ко личество элементов, определяемых параметром count. Метод возвращает индекс совпавшего элемента. Если искомое значение не найдено, воз вращается отрицательное значение. Вызывающий список должен быть отсортирован public List GetRange(int index, int count) Возвращает часть вызывающей коллекции. Часть возвращаемой коллекции начинается с элемента, указываемого по индексу index, и включает коли чество элементов, задаваемое параметром count. Возвращаемый объект ссылается на те же элемен ты, что и вызывающий объект public int IndexOf(T item) Возвращает индекс первого вхождения элемента item в вызывающей коллекции. Если искомый эле мент не обнаружен, возвращается значение -1 public void InsertRange(int index, IEnumerable 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() Сортирует вызывающую коллекцию по нарас тающей В классе List определяется также собственное свойство Capacity, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Это свойство объявля ется следующим образом. public int Capacity { get; set; } Свойство Capacity позволяет установить и получить емкость вызывающей коллек ции в качестве динамического массива. Эта емкость равна количеству элементов, кото рые может содержать коллекция до ее вынужденного расширения. Такая коллекция расширяется автоматически, и поэтому задавать ее емкость вручную необязательно. Но из соображений эффективности это иногда можно сделать, если заранее известно количество элементов коллекции. Благодаря этому исключаются издержки на выделе ние дополнительной памяти. В классе List реализуется также приведенный ниже индексатор, определен ный в интерфейсе IList. public Т this[int index] { get; set; } С помощью этого индексатора устанавливается и получается значение элемента коллекции, указываемое по индексу index. В приведенном ниже примере программы демонстрируется применение клас са List. Это измененный вариант примера, демонстрировавшего ранее класс ArrayList. Единственное изменение, которое потребовалось для этого, заключалось в замене класса ArrayList классом List, а также в использовании параметров обоб щенного типа. Окончание табл. 25.15 Метод Описание public void Sort(IСоmраrеr<Т> comparer) Сортирует вызывающую коллекцию, используя для сравнения способ, задаваемый параметром comparer. Если параметр comparer имеет пустое значение, то для сравнения используется способ, выбираемый по умолчанию public void Sort(Comparison comparison) Сортирует вызывающую коллекцию, используя для сравнения указанный делегат public void Sort (int index, int count, IComparer comparer) Сортирует вызывающую коллекцию, используя для сравнения способ, задаваемый параметром comparer. Сортировка начинается с элемента, ука зываемого по индексу index, и включает количе ство элементов, определяемых параметром count. Если параметр comparer имеет пустое значение, то для сравнения используется способ, выбираемый по умолчанию public T[] ToArray() Возвращает массив, содержащий копии элементов вызывающего объекта public void TrimExcess() Сокращает емкость вызывающей коллекции таким образом, чтобы она не превышала 10% от количе ства элементов, хранящихся в ней на данный момент // Продемонстрировать применение класса List. using System; using System.Collections.Generic; class GenListDemo { static void Main() { // Создать коллекцию в виде динамического массива. List lst = new List(); 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 Содержимое: C E B 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 В классе LinkedList создается коллекция в виде обобщенного двунаправлен ного списка. В этом классе реализуются интерфейсы ICollection, ICollection, IEnumerable, IEnumerable, ISerializable и IDeserializationCallback. В двух последних интерфейсах поддерживается сериализация списка. В классе LinkedList определяются два приведенных ниже открытых конструктора. public LinkedList() public LinkedList(IEnumerable collection) В первом конструкторе создается пустой связный список, а во втором конструкто ре – список, инициализируемый элементами из коллекции collection. Как и в большинстве других реализаций связных списков, в классе LinkedList инкапсулируются значения, хранящиеся в узлах списка, где находятся также ссылки на предыдущие и последующие элементы списка. Эти узлы представляют собой объекты класса LinkedListNode. В классе LinkedListNode предоставляются четыре следующих свойства. public LinkedListNode Next { get; } public LinkedListNode Previous { get; } public LinkedList List { get; } public T Value { get; set; } С помощью свойств Next и Previous получаются ссылки на предыдущий и после дующий узлы списка соответственно, что дает возможность обходить список в обоих направлениях. Если же предыдущий или последующий узел отсутствует, то возвра щается пустая ссылка. Для получения ссылки на сам список служит свойство List. А с помощью свойства Value можно устанавливать и получать значение, находящееся в узле списка. В классе LinkedList определяется немало методов. В табл. 25.16 приве дены наиболее часто используемые методы данного класса. Кроме того, в клас се LinkedList определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже. public LinkedListNode First { get; } public LinkedListNode Last { get; } С помощью свойства First получается первый узел в списке, а с помощью свой ства Last – последний узел в списке. Таблица 25.16. Наиболее часто используемые методы, определенные в классе LinkedList Метод Описание public LinkedListNode AddAfter(LinkedListNode node, T value) Добавляет в список узел со значением value не посредственно после указанного узла node. Указы ваемый узел node не должен быть пустым (null). Метод возвращает ссылку на узел, содержащий зна чение value public void AddAfter(LinkedListNode node, LinkedListNode newNode) Добавляет в список новый узел newNode непо средственно после указанного узла node. Ука зываемый узел node не должен быть пустым (null). Если узел node отсутствует в списке или если новый узел newNode является частью другого списка, то генерируется исключение InvalidOperationException public LinkedListNode AddBefore(LinkedListNode node, T value) Добавляет в список узел со значением value непо средственно перед указанным узлом node. Указы ваемый узел node не должен быть пустым (null). Метод возвращает ссылку на узел, содержащий зна чение value В приведенном ниже примере программы демонстрируется применение класса LinkedList. // Продемонстрировать применение класса LinkedList. using System; using System.Collections.Generic; Окончание табл. 25.16 Метод Описание public void AddBefore(LinkedListNode node, LinkedListNode newNode) Добавляет в список новый узел newNode не посредственно перед указанным узлом node. Указываемый узел node не должен быть пу стым (null). Если узел node отсутствует в спи ске или если новый узел newNode является ча стью другого списка, то генерируется исключение InvalidOperationException public LinkedList AddFirst(T value) Добавляет узел со значением value в начало спи ска. Метод возвращает ссылку на узел, содержащий значение value public void AddFirst(LinkedListNode node) Добавляет узел node в начало списка. Если узел node является частью другого списка, то генериру ется исключение InvalidOperationException public LinkedList AddLast(T value) Добавляет узел со значением value в конец спи ска. Метод возвращает ссылку на узел, содержащий значение value public void AddLast(LinkedListNode node) Добавляет узел node в конец списка. Если узел node является частью другого списка, то генериру ется исключение InvalidOperationException public LinkedList Find(T value) Возвращает ссылку на первый узел в списке, име ющий значение value. Если искомое значение value отсутствует в списке, то возвращается пустое значение public LinkedList FindLast(T value) Возвращает ссылку на последний узел в списке, имеющий значение value. Если искомое значение value отсутствует в списке, то возвращается пустое значение public bool Remove(T value) Удаляет из списка первый узел, содержащий значе ние value. Возвращает логическое значение true, если узел удален, т.е. если узел со значением value обнаружен в списке и удален; в противном случае возвращает логическое значение false public void Remove(LinkedList node) Удаляет из списка узел, соответствующий ука занному узлу node. Если узел node отсут ствует в списке, то генерируется исключение InvalidOperationException public void RemoveFirst() Удаляет из списка первый узел public void RemoveLast() Удаляет из списка последний узел class GenLinkedListDemo { static void Main() { // Создать связный список. LinkedList ll = new LinkedList(); 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 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 11) 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. Двунаправленный характер подобных связных списков имеет особое значение для приложений, управляющих базами данных, где нередко требуется перемещаться по списку в обоих направлениях. Класс Dictionary Класс Dictionary позволяет хранить пары "ключ-значение" в коллекции как в словаре. Значения доступны в словаре по соответствующим клю чам. В этом отношении данный класс аналогичен необобщенному классу Hashtable. В классе Dictionary реализуются интерфейсы IDictionary, IDictionary, ICollection, ICollection>, IEnumerable, IEnumerable>, ISerializable и IDeserializationCallback. В двух последних интерфейсах поддер живается сериализация списка. Словари имеют динамический характер, расширяясь по мере необходимости. В классе Dictionary предоставляется немало конструкторов. Ниже перечислены наиболее часто используемые из них. public Dictionary() public Dictionary(IDictionary dictionary) public Dictionary(int capacity) В первом конструкторе создается пустой словарь с выбираемой по умолчанию первоначальной емкостью. Во втором конструкторе создается словарь с указанным ко личеством элементов dictionary. А в третьем конструкторе с помощью параметра сараcity указывается емкость коллекции, создаваемой в виде словаря. Если размер словаря заранее известен, то, указав емкость создаваемой коллекции, можно исклю чить изменение размера словаря во время выполнения, что, как правило, требует до полнительных затрат вычислительных ресурсов. В классе Dictionary определяется также ряд методов. Некото рые наиболее часто используемые методы этого класса сведены в табл. 25.17. Таблица 25.17. Наиболее часто используемые методы, определенные в классе Dictionary Метод Описание 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 определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже. Свойство Описание public IEqualityComparer Comparer { get; } Получает метод сравнения для вызывающего словаря public Dictionary. KeyCollection Keys { get; } Получает коллекцию ключей public Dictionary. ValueCollection Values { get; } Получает коллекцию значений Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступ ны отдельными списками с помощью свойств Keys и Values. В коллекциях типа Dictionary.KeyCollection и Dictionary. ValueCollection реализуются как обобщенные, так и необобщенные формы интер фейсов ICoirection и IEnumerable. И наконец, в классе Dictionary реализуется приведенный ниже индексатор, определенный в интерфейсе IDictionary. public TValue this[TKey key] { get; set; } Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в качестве индекса в данном случае служит ключ элемента, а не сам индекс. При перечислении коллекции типа Dictionary из нее возвра щаются пары "ключ-значение" в форме структуры KeyValuePair. Напомним, что в этой структуре определяются два поля. public TKey Key; public TValue Value; В этих полях содержится ключ или значение соответствующего элемента коллек ции. Как правило, структура KeyValuePair не используется не посредственно, поскольку средства класса Dictionary позволяют работать с ключами и значениями по отдельности. Но при перечислении коллекции типа Dictionary, например, в цикле foreach перечисляемыми объектами являются пары типа KeyValuePair. Все ключи в коллекции типа Dictionary должны быть уни кальными, причем ключ не должен изменяться до тех пор, пока он служит в качестве ключа. В то же время значения не обязательно должны быть уникальными. К тому же объекты не хранятся в коллекции типа Dictionary в отсортирован ном порядке. В приведенном ниже примере демонстрируется применение класса Dictionary. // Продемонстрировать применение класса обобщенной // коллекции Dictionary. using System; using System.Collections.Generic; class GenDictionaryDemo { static void Main() { // Создать словарь для хранения имен и фамилий // работников и их зарплаты. Dictionary dict = new Dictionary(); // Добавить элементы в коллекцию. diet.Add("Батлер, Джон", 73000); diet.Add("Шварц, Capa", 59000); diet.Add("Пайк, Томас", 45000); diet.Add("Фрэнк, Эд", 99000); // Получить коллекцию ключей, т.е. фамилий и имен. ICollection с = diet.Keys; // Использовать ключи для получения значений, т.е. зарплаты. foreach(string str in с) Console.WriteLine("{0}, зарплата: {1:C}", str, diet[str]); } } Ниже приведен результат выполнения этой программы. Батлер, Джон, зарплата: $73,000.00 Шварц, Сара, зарплата: $59,000.00 Пайк, Томас, зарплата: $45,000.00 Фрэнк, Эд, зарплата: $99,000.00 Класс SortedDictionary В коллекции класса SortedDictionary пары "ключ-значение" хранятся таким же образом, как и в коллекции класса Dictionary, за исключением того, что они отсортированы по соответствующему ключу. В клас се SortedDictionary реализуются интерфейсы IDictionary, IDictionary, ICollection, ICollection>, IEnumerable и IEnumerable>. В классе SortedDictionary предоставляются также следующие конструкторы. public SortedDictionary() public SortedDictionary(IDictionary dictionary) public SortedDictionary(IComparer comparer) public SortedDictionary(IDictionary dictionary, IComparer comparer) В первом конструкторе создается пустой словарь, во втором конструкторе – сло варь с указанным количеством элементов dictionary. В третьем конструкторе допу скается указывать с помощью параметра comparer типа IComparer способ сравне ния, используемый для сортировки, а в четвертом конструкторе – инициализировать словарь, помимо указания способа сравнения. В классе SortedDictionary определен ряд методов. Некоторые наиболее часто используемые методы этого класса сведены в табл. 25.18. Таблица 25.18. Наиболее часто используемые методы, определенные в классе SortedDictionary Метод Описание 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 Кроме того, в классе SortedDictionary определяются собствен ные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализу ются. Эти свойства приведены ниже. Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступ ны отдельными списками с помощью свойств Keys и Values. В коллекциях типа SortedDictionary.KeyCollection и SortedDictionary.ValueCollection реализуются как обобщенные, так и необобщенные фор мы интерфейсов ICollection и IEnumerable. И наконец, в классе SortedDictionary реализуется приведен ный ниже индексатор, определенный в интерфейсе IDictionary. public TValue this[TKey key] { get; set; } Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в данном случае в качестве индекса служит ключ элемента, а не сам индекс. При перечислении коллекции типа SortedDictionary из нее возвращаются пары "ключ-значение" в форме структуры KeyValuePair. Напомним, что в этой структуре определяются два следующих поля. public TKey Key; public TValue Value; В этих полях содержится ключ или значение соответствующего элемента коллек ции. Как правило, структура KeyValuePair не используется не посредственно, поскольку средства класса SortedDictionary по зволяют работать с ключами и значениями по отдельности. Но при перечислении коллекции типа SortedDictionary, например в цикле foreach, перечисляемыми объектами являются пары типа KeyValuePair. Окончание табл. 25.18 Свойство Описание public Icomparer Comparer { get; } Получает метод сравнения для вызы вающего словаря public SortedDictionary. KeyCollection Keys { get; } Получает коллекцию ключей public SortedDictionary. ValueCollection Values { get; } Получает коллекцию значений Все ключи в коллекции типа SortedDictionary должны быть уникальными, причем ключ не должен изменяться до тех пор, пока он служит в каче стве ключа. В то же время значения не обязательно должны быть уникальными. В приведенном ниже примере демонстрируется применение класса SortedDictionary. Это измененный вариант предыдущего приме ра, демонстрировавшего применение класса Dictionary. В данном варианте база данных работников отсортирована по фамилии и имени работника, ко торые служат в качестве ключа. // Продемонстрировать применение класса обобщенной // коллекции SortedDictionary. using System; using System.Collections.Generic; class GenSortedDictionaryDemo { static void Main() { // Создать словарь для хранения имен и фамилий // работников и их зарплаты. SortedDictionary dict = new SortedDictionary(); // Добавить элементы в коллекцию. dict.Add("Батлер, Джон", 73000); dict.Add("Шварц, Capa", 59000); dict.Add("Пайк, Томас", 45000); dict.Add("Фрэнк, Эд", 99000); // Получить коллекцию ключей, т.е. фамилий и имен. ICollection с = dict.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 В коллекции класса SortedList хранится отсортированный спи сок пар "ключ-значение". Это обобщенный эквивалент класса необобщенной коллекции SortedList. В классе SortedList реализуются интерфейсы IDictionary, IDictionary, ICollection, ICollection>, IEnumerable и IEnumerable>. Размер коллекции типа SortedList изменяется динамически, автоматически увеличиваясь по мере необходимости. Класс SortedList подобен классу SortedDictionary, но у него другие рабочие характеристики. В част ности, класс SortedList использует меньше памяти, тогда как класс SortedDictionary позволяет быстрее вставлять неупорядоченные эле менты в коллекцию. В классе SortedList предоставляется немало конструкторов. Ниже перечислены наиболее часто используемые конструкторы этого класса. public SortedList() public SortedList(IDictionary dictionary) public SortedList(int capacity) public SortedList(IComparer comparer) В первой форме конструктора создается пустой список с выбираемой по умолча нию первоначальной емкостью. Во второй форме конструктора создается отсортиро ванный список с указанным количеством элементов dictionary. В третьей форме конструктора с помощью параметра capacity задается емкость коллекции, созда ваемой в виде отсортированного списка. Если размер списка заранее известен, то, ука зав емкость создаваемой коллекции, можно исключить изменение размера списка во время выполнения, что, как правило, требует дополнительных затрат вычислительных ресурсов. И в четвертой форме конструктора допускается указывать с помощью пара метра comparer способ сравнения объектов, содержащихся в списке. Емкость коллекции типа SortedList увеличивается автоматиче ски по мере необходимости, когда в список добавляются новые элементы. Если теку щая емкость коллекции превышается, то она увеличивается. Преимущество указания емкости коллекции типа SortedList при ее создании заключает ся в снижении или полном исключении издержек на изменение размера коллекции. Разумеется, указывать емкость коллекции целесообразно лишь в том случае, если за ранее известно, сколько элементов требуется хранить в ней. В классе SortedList определяется ряд собственных методов, по мимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Некоторые из наиболее часто используемых методов этого класса перечислены в табл. 25.19. Сле дует иметь в виду, что перечислитель, возвращаемый методом GetEnumerator(), слу жит для перечисления пар "ключ-значение", хранящихся в отсортированном списке в виде объектов типа KeyValuePair. Таблица 25.19. Наиболее часто используемые методы, определенные в классе SortedList Метод Описание public void Add(TKey key, TValue value) Добавляет в список пару "ключ-значение”, определяемую параметрами key и value. Если ключ key уже находится в списке, то его значение не изменяется, и генерируется исклю чение ArgumentException public bool ContainsKey(TK key) Возвращает логическое значение true, если вы зывающий список содержит объект key в каче стве ключа; а иначе – логическое значение false Окончание табл. 25.19 Кроме того, в классе SortedList определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свой ства приведены ниже. И наконец, в классе SortedList реализуется приведенный ниже индексатор, определенный в интерфейсе IDictionary. Это еще один измененный вариант представленного Метод Описание public bool ContainsValue(TValue value) Возвращает логическое значение true, если вызывающий список содержит значение value; в противном случае – логическое зна чение false public IEnumerator