Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Универсальные коллекции языка С#. Учебное пособие
.pdf
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(newRecord(2,2));
set.Add(newRecord(6,4));
Console.WriteLine("set=");
//Выводимэементымножестванаэкран.
foreach(varelinset)Console.WriteLine(el.ToString());
if(!set.Add(newRecord(2,2)))
Console.WriteLine("Элемент(2,2)ужеестьвомножестве!");
Console.WriteLine($"Элементов:{set.Count},min={set.Min},max={set.Max}");
//Создаемперечислениедляпроходапомножествувобратномпорядке.
varreorder=set.Reverse<Record>();
Console.WriteLine($"Выводимэлементымножествавобратномпорядке.");
foreach(varelinreorder)
Console.WriteLine(el.ToString());
//
intc=set.Count(el=>el.im==el.re);
Console.WriteLine($"Числоэлементовсодинаковымизначениямиполей={c}.");
//Возвращаетизмножестваэлементы,покаусловиевыполняется.
IEnumerable<Record>t=set.TakeWhile(p=>p.re<3);
Console.WriteLine($"Элементы,удовлетворяющиеусловиюполеre<3.");
foreach(varelint)
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.MySortedSetComparer<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

staticvoidMain(string[]args)
{
//Создаемупорядоченноемножество.Вызываемконструкторбезпараметров.
//ИнициализируеммножествотремяобъектамиRecord.
SortedSet<Record>set=newSortedSet<Record>()
{
newRecord(1,2),
newRecord(3,3),
newRecord(4,3),
};
//ДобавляемвомножествообъектRecord.
set.Add(newRecord(2,2));
Console.WriteLine("set=");
//Выводимэементымножестванаэкран.
foreach(varelinset)Console.WriteLine(el.ToString());
//
//Задаемдлянегоновоеправилоупорядоченияпополюim.
varset1=newSortedSet<Record>(set,new
Record.MySortedSetComparer<Record>());
Console.WriteLine("set1=");
//Выводимэементымножестванаэкран.
foreach(varelinset1)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 ValuePair<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 и IDeserializationCallback. В двух последних интерфейсах поддерживается сериализация списка.
Классе 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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
