Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Универсальные коллекции языка С#. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq;
namespaceExample_0 {
classProgram { staticvoidMain(string[]args) { //Создаемупорядоченноемножество.Вызываемконструкторбезпараметров. //ИнициализируеммножествотремяобъектамиRecord. SortedSet<Record>set=newSortedSet<Record>() { newRecord(1,2), newRecord(3,3), newRecord(4,3), }; //ДобавляемвомножествообъектыRecord. set.Add(newRecord(2,2));
set.Add(newRecord(6,4));
 Console.WriteLine("set="); //Выводимэементымножестванаэкран. foreach(varelinset)Console.WriteLine(el.ToString()); if(!set.Add(newRecord(2,2))) Console.WriteLine("Элемент(2,2)ужеестьвомножестве!"); Console.WriteLine($"Элементов:{set.Count},min={set.Min},max={set.Max}"); //Создаемперечислениедляпроходапомножествувобратномпорядке. varreorder=set.Reverse<Record>(); Console.WriteLine($"Выводимэлементымножествавобратномпорядке."); foreach(varelinreorder) Console.WriteLine(el.ToString()); // intc=set.Count(el=>el.im==el.re); Console.WriteLine($"Числоэлементовсодинаковымизначениямиполей={c}."); //Возвращаетизмножестваэлементы,покаусловиевыполняется. IEnumerable<Record>t=set.TakeWhile(p=>p.re<3); Console.WriteLine($"Элементы,удовлетворяющиеусловиюполеre<3."); foreach(varelint) Console.WriteLine(el.ToString()); } } }
Находимколичествоэлементов,укоторыхобаполяравны.
71
Результатработыпримера: set= (1,2) (2,2) (3,3) (4,3) (6,4)
Элемент(2,2)ужеестьвомножестве! Элементов:5,min=(1,2),max=(6,4) Выводимэлементымножествавобратномпорядке.
(6,4) (4,3) (3,3) (2,2) (1,2)
Числоэлементовсодинаковымизначениямиполей=2. Элементы,удовлетворяющиеусловиюполеre<3.
(1,2) (2,2)
В следующем примере создается и инициализируется объект set класса SortedSet<T> с помощью конструктора без параметров. При со­здании set инициализируется тремя объектами класса Record с по­мощью списка инициализаторов, затем в него добавляется объект с помощью метода Add и элементы множества выводятся на консоль. Затем создается объект set1 класса SortedSet<T> и инициализируется с помощью конструктора, который принимает
два параметра IEnu-
merable<T>, IComparer<T>. Первым параметром передается множество set, вторым параметром передается метод Record.MySortedSetCom­parer<Record>(), используемый для установки порядка сортировки
элементов множества set1 по полю im. Элементы созданного множе­ства выводятся на консоль.
publicclassRecord:IComparable<Record>,IComparer<Record> { publicdoublere{get;} publicdoubleim{get;}  publicRecord(doubler,doublei) { re=r;im=i; } publicdoubleModule()
72
{ returnMath.Sqrt(re*re+im*im); } //СравнениеобъектовRecordпомодулю. publicintCompare(Recordx,Recordy) { if(x.Module()<y.Module()) { return‐1; } else { if(x.Module()==y.Module())return0; elsereturn1; } } //Сравнениеобъектов publicintCompareTo(Recordx) {  returnCompare(this,x); } //ОтображениеобъектовRecordвстроку. publicoverridestringToString() { return$"({re.ToString()},{im.ToString()})"; } //КлассреализуеткомпаратордлясравненияобъектовRecord. publicclassMySortedSetComparer<T>:IComparer<Record> { //СравнениеобъектовRecord. publicintCompare(Recordx,Recordy) { if(x.im<y.im) elseif(x.im>y.im)return1; return0; } }
Record.
return‐1;
usingSystem; usingSystem.Collections.Generic;
classProgram {
73
staticvoidMain(string[]args) { //Создаемупорядоченноемножество.Вызываемконструкторбезпараметров. //ИнициализируеммножествотремяобъектамиRecord. SortedSet<Record>set=newSortedSet<Record>() { newRecord(1,2), newRecord(3,3), newRecord(4,3), }; //ДобавляемвомножествообъектRecord. set.Add(newRecord(2,2)); Console.WriteLine("set="); //Выводимэементымножестванаэкран. foreach(varelinset)Console.WriteLine(el.ToString()); // //Задаемдлянегоновоеправилоупорядоченияпополюim. varset1=newSortedSet<Record>(set,new Record.MySortedSetComparer<Record>()); Console.WriteLine("set1="); //Выводимэементымножестванаэкран. foreach(varelinset1)Console.WriteLine(el.ToString()); } } } Результатработыпримера:
set= (1,2) (2,2) (3,3) (4,3) set1= (1,2) (3,3)
Создаемновоемножество,наосновемножестваset.
УНИВЕРСАЛЬНЫЕ СЛОВАРИ – КОЛЛЕКЦИИ
DICTIONARY<TKEY,TVALUE>
И SORTEDDICTIONARY<TKEY,TVALUE>
Универсальные словари в C# представлены двумя универсальными классами коллекций:
классом Dictionary<TKey,TValue> – словарь;
классом SortdDictionary<TKey,TValue> – сортированный словарь.
74
Универсальные словари представляют коллекции ключей и значе­ний. Здесь TKey – тип ключей в словаре; TValue – тип значений в сло­варе. Словари относятся к динамическим коллекциям, они могут рас­ширяться по мере необходимости.
При последовательном обращении к элементам словарей (напри­мер, в цикле forech) из них извлекаются пары «ключ-значение» в фор­ме объекта структуры
KeyValuePair<TKey, TValue>. В этой структуре
определены два поля:
publicTKeyKey;
publicTValueValue;
В этих полях содержится ключ и значение соответствующего эле­мента коллекции. Как правило, структура KeyValuePair<TKey, TValue> не используется непосредственно, поскольку средства словарей позво­ляют работать с ключами и значениями по отдельности.
В классах универсальных словарей реализуются интерфейсы IDic-
tionary, IDictionary<TKey, TValue>, ICollection, ICollection<Key Value­Pair<TKey, TValue>>, IEnumerable и IEnumerable<KeyValuePair<TKey, TValue>>.
Универсальные словари реализуют как обобщенные, так и необоб­щенные формы интерфейсов ICollection и IEnumerable.
В классах универсальных словарей определены следующие свойства.
Свойство Назначение
Comparer
Count
Item[TKey]
Keys
Для SortedDictionary<TKey,TValue> возвращает интерфейс IEqualityComparer<T>, используемый для проверки равенства
ключей словаря Для SortedDictionary<TKey,TValue> возвращает интерфейс
IComparer<T>, используемый для упорядочения элементов словаря
Возвращает число пар «ключ-значение», содержащихся в словаре
Возвращает или задает значение, связанное с указанным клю­чом
Возвращает коллекцию, содержащую ключи из словаря
75
Ключи и значения, содержащиеся в универсальных словарях, до­ступны отдельными списками с помощью свойств Keys и Values.
В универсальных словарях реализуется приведенный ниже ин­дексатор, определенный в интерфейсе IDictionary<TKey, TValue>
publicTValuethis[TKeykey]{get;set;}
Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента, но в качестве индекса в данном случае служит ключ элемента, а не сам ин­декс.
В универсальных словарях определен ряд методов.
Методы Назначение
Add()
ContainsKey()
ContainsValue()
Remove()
Добавляет в словарь пару «ключ-значение», определяемую параметрами key и value. Если ключ key уже находится в словаре, то его значение не изменяется, и генерируется исключение ArgumentException
Возвращает логическое значение true, если вызывающий словарь содержит объект key в качестве ключа, а иначе – логическое значение false
Возвращает логическое значение true, если вызывающий словарь содержит значение value; в противном случае – логическое значение false
Удаляет ключ key из словаря. При удачном исходе опера­ции возвращается логическое значение true, а если ключ key отсутствует в словаре – логическое значение false
КЛАСС DICTIONARY<TKEY,TVALUE>
Класс Dictionary<TKey,TValue> – словарь представляет коллекцию ключей и значений. Здесь TKey – тип ключей в словаре; TValue – тип значений в словаре.
Универсальный класс Dictionary<TKey,TValue> обеспечивает сопоставление набора ключей с набором значений. Каждый элемент, добавляемый в словарь, состоит из значения TValue и связанного
76
с ним ключа TKey. Получение значения с помощью его ключа вы­полняется очень быстро, с временной сложностью близкой к O(1), поскольку класс Dictionary<TKey,TValue> реализуется как хэш­таблица.
Скорость извлечения зависит от качества алгоритма хэширования для типа, указанного для TKey.
Объект, используемый в качестве ключа в Dictionary<TKey, TValue>, не должен изменяться каким-либо образом, который влияет на
значение его хэша. Каждый ключ в Dictionary<TKey,TValue> дол­жен быть уникальным в соответствии с компаратором на равенство словаря. Ключ не может быть null, но значение может быть, если его тип TValue является ссылочным типом.
Классу Dictionary<TKey,TValue> для определения того, равны ли ключи, требуется реализация операции равенства. Реализацию универ­сального интерфейса IEqualityComparer<T> можно передать конструк­тору, принимающему
параметр comparer. Если не указать реализацию, будет использоваться компаратор универсального равенства по умол­чанию EqualityComparer<T>.Default. Если тип TKey реализует универ­сальный интерфейс System.IEquatable<T>, компаратор проверки на равенство по умолчанию использует эту реализацию.
Словарь представляет собой сложную структуру данных, поз­воляющую обеспечить доступ к элементам по ключу. Главное свой­ство словарей – быстрый поиск на основе
ключей. Можно также свободно добавлять и удалять элементы, подобно тому, как это дела­ется в List<T>, но без накладных расходов производительности, связанных с необходимостью смещения последующих элементов в памяти.
На рисунке показана упрощенная модель словаря. Здесь ключами словаря служат идентификаторы сотрудников, такие как А11. Ключ трансформируется в хеш. В хеше создается число
для ассоциации ин­декса со значением. После этого индекс содержит ссылку на значение. Изображенная модель является упрощенной, поскольку существует возможность того, что единственное вхождение индекса может быть ассоциировано с несколькими значениями, и индекс может храниться в виде дерева.
77
Ключи Индекс
0
Значения
А11
С20
Т32
1
2
50
60
101
102
А11 Белов Иван
С20 Титов Вадим
Т32
Фомин Петр
Тип, используемый в качестве ключа словаря, должен переопреде­лять метод GetHashCode() класса Object. Всякий раз, когда необходимо найти местоположение элемента в словаре, вызывается метод
GetHashCode().
Целое число, возвращаемое этим методом, используется словарем для вычисления индекса, под которым необходимо поместить в него элемент.
Реализация метода GetHashCode() должна удовлетворять перечис­ленным ниже требованиям.
Один и тот же объект должен всегда возвращать одно и то же
значение.
Разные объекты могут возвращать одно и то же значение.
Он должен выполняться насколько возможно быстро, не требуя
значительных вычислительных затрат.
Он не должен генерировать исключений.
Он должен использовать как минимум одно поле экземпляра.
Значения хеш-кода должны распределяться равномерно по всему
диапазону чисел, которые может хранить int.
Хеш-код не должен изменяться на протяжении времени суще-
ствования объекта.
78
Чем вызвана необходимость равномерного распределения значений хеш-кода по диапазону целых чисел? Если два ключа возвращают хеш­значения, дающие один и тот же индекс, класс словаря вынужден ис­кать ближайшее доступное свободное место для сохранения второго элемента, к тому же ему придется выполнять некоторый поиск, чтобы впоследствии извлечь требуемое значение. Это
наносит ущерб произ­водительности, и если множество ключей дают одни и те же индексы, под которыми их следует поместить в словарь, вероятность конфлик­тов значительно возрастает. Однако благодаря способу, которым рабо­тает часть алгоритма, принадлежащая Microsoft, риск снижается до минимума, когда вычисляемое значение хеш-кода равномерно распре­делено между int.MinValue и int.MaxValue.
Помимо
реализации GetHashCode() тип ключа должен также реали­зовывать метод IEquatable<T>.Equals() либо переопределять метод Equals() класса Object. Поскольку разные объекты ключа могут воз­вращать один и тот же хеш-код, метод Equals() используется при срав­нении ключей словаря. Словарь проверяет два ключа А и В на эквива­лентность, вызывая A.Equals(В). Это означает, что потребуется обеспечить истинность
следующего утверждения:
Если истинно А.Equals(В), значит, А.GetHashCode() и В.GetHash
Code() всегда должны возвращать один и тот же хеш-код.
В классе Dictionary<TKey, TValue>, помимо общих для универ­сальных словарей, реализуются интерфейсы ISerializable и IDeserializa­tionCallback. В двух последних интерфейсах поддерживается сериали­зация списка.
Классе Dictionary<TKey, TValue> предоставляет несколько кон­структоров. Ниже перечислены наиболее часто используемые из
publicDictionary() publicDictionary(IDictionary<TKey,TValue>dictionary) publicDictionary(intcapacity)
них:
В первом конструкторе создается пустой словарь с выбираемой по умолчанию первоначальной емкостью. Во втором конструкторе созда­ется словарь, элементы которого копируются из dictionary. В третьем конструкторе с помощью параметра capacity указывается емкость кол­лекции, создаваемой в виде словаря. Если размер словаря заранее из­вестен, то, указав емкость создаваемой коллекции, можно исключить
79
изменение размера словаря во время выполнения, что, как правило, требует дополнительных затрат вычислительных ресурсов.
В следующем примере кода создается словарь (Dictionary<TKey, TValue>) со строковым ключом и целым значением, инициализиро­ванный пятью записями. Метод Add используется для добавления двух новых элементов. В примере показано, что метод Add создает исклю­чение ArgumentException при попытке добавления повторяющегося ключа.
В примере Item[] свойство (индексатор в C#) используется для из­влечения значений, что демонстрирует, что KeyNotFoundException­исключение возникает, когда запрошенный ключ отсутствует, и пока­зывает, что значение, связанное с ключом, можно заменить.
В примере показано использование TryGetValue метода как более эффективного способа получения значений, если программа часто пы­тается обращаться к значениям ключей, которых нет
в словаре, а также показано, как использовать ContainsKey-метод для проверки наличия ключа перед вызовом Add-метода.
В примере показано, как перечислить ключи и значения в словаре, а также как перечислить только ключи и только значения с помощью Keys и Values свойств.
Наконец, в примере демонстрируется Remove метод для удаления
записей из словаря.
usingSystem; usingSystem.Collections.Generic;
namespaceПример_0 {
classProgram { //Методдлявыводаотсортированногословарянаэкран. staticvoidPrint<KeyValuePare>(inIEnumerable<KeyValuePare>cnt,stringheader) { Console.WriteLine(header); foreach(KeyValueParepareincnt) { Console.WriteLine(pare.ToString()); } Console.WriteLine(); }
80
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]