Текст книги "C# 4.0: полное руководство"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 77 (всего у книги 83 страниц)
В пространстве имен System.Collections.Generic определен целый ряд интерфейсов обобщенных коллекций, имеющих соответствующие аналоги среди интерфейсов необобщенных коллекций. Все эти интерфейсы сведены в табл. 25.10.
Таблица 25.10. Интерфейсы обобщенных коллекций
Интерфейс – Описание
lCollection
IComparer
IEnumerable
Enumerator
IEqualityComparer
IList
Интерфейс ICollection
В интерфейсе ICollection
определен ряд свойств, которые являются общими для всех обобщенных коллекций. Интерфейс ICollection
является обобщенным вариантом необобщенного интерфейса ICollection
, хотя между ними имеются некоторые отличия.
Итак, в интерфейсе ICollection
определены следующие свойства.
int Count { get; }
bool IsReadOnly { get; }
Свойство Count
содержит ряд элементов, хранящихся в данный момент в коллекции. А свойство IsReadOnly
имеет логическое значение true
, если коллекция доступна только для чтения. Если же коллекция доступна как для чтения, так и для записи, то данное свойство имеет логическое значение false
.
Кроме того, в интерфейсе ICollection
определены перечисленные ниже методы. Обратите внимание на то, что в этом обобщенном интерфейсе определено несколько большее количество методов, чем в его необобщенном аналоге.
Метод – Описание
void Add(Titem) – Добавляет элемент item в вызывающую коллекцию. Генерирует исключение NotSupportedException, если коллекция доступна только для чтения
void Clear() – Удаляет все элементы из вызывающей коллекции
bool Contains(Titem) – Возвращает логическое значение true, если вызывающая коллекция содержит элемент item, а иначе – логическое значение false
void CopyTo(T[]array,intarraylndex) – Копирует содержимое вызывающей коллекции в массив array, начиная с элемента, указываемого по индексу arraylndex
void Remove(Titem) – Удаляет первое вхождение элемента item в вызывающей коллекции. Возвращает логическое значение true, если элемент i tem удален. А если этот элемент не найден в вызывающей коллекции, то возвращается логическое значение false
Некоторые из перечисленных выше методов генерируют исключение NotSupportedException
, если коллекция доступна только для чтения.
А поскольку интерфейс 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(intindex, Titem) – Вставляет в вызывающую коллекцию элемент item по индексу index
void RemoveAt(intindex) – Удаляет из вызывающей коллекции элемент, расположенный по указанному индексу index
Кроме того, в интерфейсе IList
определяется индексатор
Т this[int index] { get; set; }
который устанавливает или возвращает значение элемента коллекции по указанному индексу index.
Интерфейс IDictionary
В интерфейсе IDictionary
Таблица 25.12. Методы, определенные в интерфейсе IDictionaryCTKey, TValue>
Метод – Описание
void Add(TKeykey, TValuevalue) – Добавляет в вызывающую коллекцию пару “ключ-значение”, определяемую параметрами key и value. Генерирует исключение ArgumentException, если ключ key уже находится в коллекции
bool Contains(TKeykey) – Возвращает логическое значение true, если вызывающая коллекция содержит элемент key в качестве ключа, а иначе – логическое значение false
bool Remove(TKeykey) – Удаляет из коллекции элемент, ключ которого равен значению key
bool TryGetValue(TKeykey, out TValuevalue) – Предпринимает попытку извлечь значение из коллекции по указанному ключу key и присвоить это значение переменной value. При удачном исходе операции возвращается логическое значение true, а иначе – логическое значение false. Если ключ key не найден, переменной value присваивается значение, выбираемое по умолчанию
Кроме того, в интерфейсе IDictionary
определены перечисленные ниже свойства.
Свойство – Описание
ICollection Keys
ICollection Values
Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступны отдельными списками с помощью свойств Keys
и Values
.
И наконец, в интерфейсе IDictionary
определяется следующий индексатор.
TValue this[TKey key] { get; set; }
Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Следует, однако, иметь в виду, что в качестве индекса в данном случае служит ключ элемента, а не сам индекс.
Интерфейсы IEnumerable и IEnumerator
Интерфейсы IEnumerable
и IEnumerator
являются обобщенными эквивалентами рассмотренных ранее необобщенных интерфейсов IEnumerable
и IEnumerator
. В них объявляются аналогичные методы и свойства, да и действуют они по тому же принципу. Разумеется, обобщенные интерфейсы оперируют данными только того типа, который указывается в аргументе типа.
В интерфейсе IEnumerable
метод GetEnumerator()
объявляется следующим образом.
IEnumerator
Этот метод возвращает перечислитель типа Т для коллекции. А это означает, что он возвращает типизированный перечислитель.
Кроме того, в интерфейсе 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
void IntersectWith(IEnumerable
bool IsProperSubsetOf(IEnumerable
bool IsProperSupersetOf(lEnumerable
bool IsSubsetOf(IEnumerable
bool IsSupersetOf(IEnumerable
bool Overlaps(IEnumerable
bool SetEquals(IEnumerable
void SymmetricExceptWith (IEnumerable
void UnionWith(IEnumerable
В пространстве имен 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
HashSet
LinkedList
List
Queue
SortedDictionary
SortedList
SortedSet
Stack
–
ПРИМЕЧАНИЕ
В пространстве имен System.Collections.Generic
находятся также следующие классы: класс SynchronizedCollection
синхронизированной коллекции на основе класса IList
; класс SynchronizedReadOnlyCollection
, доступной только для чтения синхронизированной коллекции на основе класса lList
абстрактный класс SynchronizedKeyCollectionc
служащий в качестве базового для класса коллекции System.ServiceModel.UriSchemeKeyedCollection
; а также класс KeyedByTypeCollection
коллекции, в которой в качестве ключей используются отдельные типы данных.
–
Класс List
В классе List
реализуется обобщенный динамический массив. Он ничем принципиально не отличается от класса необобщенной коллекции ArrayList
. В этом классе реализуются интерфейсы ICollection, ICollection
и IEnumerable
. У класса List
имеются следующие конструкторы.
public List()
public List(IEnumerable
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(Titem) – Выполняет поиск в вызывающей коллекции значения, задаваемого параметром item. Возвращает индекс совпавшего элемента. Если искомое значение не найдено, возвращается отрицательное значение. Вызывающий список должен быть отсортирован
public int BinarySearch(T item, IComparer
public int BinarySearch(int index, intcount,Titem, IComparer
public List
public int IndexOf(Titem) – Возвращает индекс первого вхождения элемента item в вызывающей коллекции. Если искомый элемент не обнаружен, возвращается значение -1
public void InsertRange(int index, IEnumerable
public int LastlndexOf(T item) – Возвращает индекс последнего вхождения элемента item в вызывающей коллекции. Если искомый элемент не обнаружен, возвращается значение -1
public void RemoveRange(int index, intcount) – Удаляет часть вызывающей коллекции, начиная с элемента, указываемого по индексу index, и включая количество элементов, определяемое параметром count
public void Reverse() – Располагает элементы вызывающей коллекции в обратном порядке
public void Reverse(int index, intcount) – Располагает в обратном порядке часть вызывающей коллекции, начиная с элемента, указываемого по индексу index, и включая количество элементов, определяемое параметром count
public void Sort() – Сортирует вызывающую коллекцию по нарастающей
public void Sort(IComparer
public void Sort(Comparison
public void Sort(intindex, intcount,IComparer
public T [ ] ToArray() – Возвращает массив, содержащий копии элементов вызывающего объекта
public void TrimExcess() – Сокращает емкость вызывающей коллекции таким образом, чтобы она не превышала 10% от количества элементов, хранящихся в ней на данный момент
В классе List
определяется также собственное свойство Capacity
, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Это свойство объявляется следующим образом.
public int Capacity { get; set; }
Свойство Capacity
позволяет установить и получить емкость вызывающей коллекции в качестве динамического массива. Эта емкость равна количеству элементов, которые может содержать коллекция до ее вынужденного расширения. Такая коллекция расширяется автоматически, и поэтому задавать ее емкость вручную необязательно. Но из соображений эффективности это иногда можно сделать, если заранее известно количество элементов коллекции. Благодаря этому исключаются издержки на выделение дополнительной памяти.
В классе List
реализуется также приведенный ниже индексатор, определенный в интерфейсе IList
.
public Т this[int index] { get; set; }
С помощью этого индексатора устанавливается и получается значение элемента коллекции, указываемое по индексу index.
В приведенном ниже примере программы демонстрируется применение класса List
. Это измененный вариант примера, демонстрировавшего ранее класс ArrayList
. Единственное изменение, которое потребовалось для этого, заключалось в замене класса ArrayList
классом List
, а также в использовании параметров обобщенного типа.
// Продемонстрировать применение класса List
using System;
using System.Collections.Generic;
class GenListDemo {
static void Main() {
// Создать коллекцию в виде динамического массива.
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
Содержимое: С Е В 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
и IDeserializationCallback
. В двух последних интерфейсах поддерживается сериализация списка. В классе LinkedList
public LinkedListO
public LinkedList(IEnumerable
В первом конструкторе создается пустой связный список, а во втором конструкторе – список, инициализируемый элементами из коллекции collection.
Как и в большинстве других реализаций связных списков, в классе LinkedList
инкапсулируются значения, хранящиеся в узлах списка, где находятся также ссылки на предыдущие и последующие элементы списка. Эти узлы представляют собой объекты класса LinkedListNode
. В классе LinkedListNode
предоставляются четыре следующих свойства.
public LinkedListNode
public LinkedListNode
public LinkedList
public T Value { get; set; }
С помощью свойств Next
и Previous
получаются ссылки на предыдущий и последующий узлы списка соответственно, что дает возможность обходить список в обоих направлениях. Если же предыдущий или последующий узел отсутствует, то возвращается пустая ссылка. Для получения ссылки на сам список служит свойство List
. А с помощью свойства Value
можно устанавливать и получать значение, находящееся в узле списка.
В классе LinkedList
определяется немало методов. В табл. 25.16 приведены наиболее часто используемые методы данного класса. Кроме того, в классе LinkedList
определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже.
public LinkedListNode
public LinkedListNode
С помощью свойства First
получается первый узел в списке, а с помощью свойства Last
– последний узел в списке.
Таблица 25.16. Наиболее часто используемые методы, определенные в классе LinkedList
Метод – Описание
public LinkedListNode
public void AddAfter(LinkedListNode
средственно после указанного узла node. Указываемый узел node не должен быть пустым (null). Если узел node отсутствует в списке или если новый узел newNode является частью другого списка, то* генерируется исключение InvalidOperationException
public LinkedListNode
средственно перед указанным узлом node. Указываемый узел node не должен быть пустым (null). Метод возвращает ссылку на узел, содержащий значение value
public void AddBefore(LinkedListNode
посредственно перед указанным узлом node. Указываемый узел node не должен быть пустым (null). Если узел node отсутствует в списке или если новый узел newNode является частью другого списка, то генерируется исключение InvalidOperationException
public LinkedList
public void AddFirst(LinkedListNode node) – Добавляет узел node в начало списка. Если узел node является частью другого списка, то генерируется исключение InvalidOperationException
public LinkedList
public void AddLast(LinkedListNodenode) – Добавляет узел node в конец списка. Если узел node является частью другого списка, то генерируется исключение InvalidOperationException
public LinkedList
public LinkedList
public bool Remove(T value) – Удаляет из списка первый узел, содержащий значение value. Возвращает логическое значение true, если узел удален, т.е. если узел со значением value обнаружен в списке и удален; в противном случае возвращает логическое значение false
public void Remove(LinkedList
public void RemoveFirst() – Удаляет из списка первый узел
public void RemoveLast() – Удаляет из списка последний узел
В приведенном ниже примере программы демонстрируется применение класса LinkedList
// Продемонстрировать применение класса LinkedList
using System;
using System.Collections.Generic;
class GenLinkedListDemo {
static void Main() {
// Создать связный список.
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
Console.Write("Отобразить содержимое списка по ссылкам: ");
for(node = ll.First; node != null; node = node.Next)
Console.Write(node.Value + " ");
Console.WriteLine(«n») ;
// Отобразить связный список, обойдя его в цикле foreach.
Console.Write("Отобразить содержимое списка в цикле foreach: ");