Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Руководство_по_C#.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
10.01 Mб
Скачать

12.10.1Тип ключа

Тип, используемый в качестве ключа словаря, должен переопределять метод GetHashCode() класса Object. Всякий раз, когда класс словаря должен найти местоположение элемента, он вызывает метод GetHashCode().

Целое число, возвращаемое этим методом, используется словарем для вычисления индекса, куда помещен элемент. Мы не станем углубляться в подробности работы этого алгоритма. Единственное, что следует знать — это то, что он использует простые числа, так что емкость словаря всегда выражается простым числом.

Реализация метода GetHashCode() должна удовлетворять перечисленным ниже требованиям:

  • Один и тот же объект должен всегда возвращать одно и то же значение.

  • Разные объекты могут возвращать одно и то же значение.

  • Он должен выполняться насколько возможно быстро, не требуя значительных вычислительных затрат.

  • Он не должен генерировать исключений.

  • Он должен использовать как минимум одно поле экземпляра.

  • Значения хеш-кода должны распределяться равномерно по всему диапазону чисел, которые может хранить int.

  • Хеш-код не должен изменяться на протяжении времени существования объекта.

Чем вызвана необходимость равномерного распределения значений хеш-кода по диапазону целых чисел? Если два ключа возвращают хеш-значения, дающие один и тот же индекс, класс словаря вынужден искать ближайшее доступное свободное место для сохранения второго элемента, к тому же ему придется выполнять некоторый поиск, чтобы впоследствии извлечь требуемое значение. Понятно, что это наносит ущерб производительности, и если множество ключей дают одни и те же индексы, куда их следует поместить, вероятность конфликтов значительно возрастает. Однако благодаря способу, которым работает часть алгоритма, принадлежащая Microsoft, риск снижается до минимума, когда вычисляемое значение хеш-кода равномерно распределено между int.MinValue и int.MaxValue.

Помимо реализации GetHashCode() тип ключа также должен реализовывать метод IEquatable<T>.Equals() либо переопределять метод Equals() класса Object. Поскольку разные объекты ключа могут возвращать один и тот же хеш-код, метод Equals() используется при сравнении ключей словаря. Словарь проверяет два ключа А и В на эквивалентность, вызывая A.Equals(В). Это означает, что потребуется обеспечить истинность следующего утверждения:

Если истинно А.Equals(В) , значит, А.GetHashCode() и В.GetHashCode() всегда должны возвращать один и тот же хеш-код.

12.10.2Класс Dictionary<TKey, TValue>

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

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

public Dictionary()

public Dictionary(IDictionary<TKey, TValue> dictionary)

public Dictionary(int capacity)

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

В классе Dictionary<TKey, TValue> определяется также ряд методов:

Add()

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

ContainsKey()

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

ContainsValue()

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

Remove()

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

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

Comparer

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

Keys

Получает коллекцию ключей

Values

Получает коллекцию значений

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

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

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

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

public TKey Key;

public TValue Value;

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

Давайте рассмотрим пример использования словарей:

using System;

using System.Collections.Generic;

namespace ConsoleApplication1

{

class UserInfo

{

// Метод, реализующий словарь

public static Dictionary<int, string> MyDic(int i)

{

Dictionary<int, string> dic = new Dictionary<int, string>();

Console.WriteLine("Введите имя сотрудника: \n");

string s;

for (int j = 0; j < i; j++)

{

Console.Write("Name{0} --> ", j);

s = Console.ReadLine();

dic.Add(j, s);

Console.Clear();

}

return dic;

}

}

class Program

{

static void Main()

{

Console.Write("Сколько сотрудников добавить? ");

try

{

int i = int.Parse(Console.ReadLine());

Dictionary<int, string> dic = UserInfo.MyDic(i);

// Получить коллекцию ключей

ICollection<int> keys = dic.Keys;

Console.WriteLine("База данных содержит: ");

foreach (int j in keys)

Console.WriteLine("ID -> {0} Name -> {1}", j, dic[j]);

}

catch (FormatException)

{ Console.WriteLine("Неверный ввод"); }

Console.ReadLine();

}

}

}

Выполнение программы:

База данных содержит:

ID -> 0 Name -> Виталий

ID -> 1 Name -> Александр

ID -> 2 Name -> Юля

ID -> 3 Name -> Петро

12.11Сортированный словарь: класс SortedDictionary<TKey, TValue>

Класс SortedDictionary<TKey, Tvalue> представляет дерево бинарного поиска, в котором все элементы отсортированы на основе ключа. Тип ключа должен реализовать интерфейс IComparable<TKey>. Если тип ключа не сортируемый, компаратор можно также создать, реализовав IComparer<TKey> и указав его в качестве аргумента конструктора сортированного словаря.

Классы SortedDictionary<TKey, Tvalue> и SortedList<TKey, TValue> имеют схожую функциональность. Но поскольку SortedList<TKey, TValue> реализован в виде списка, основанного на массиве, a SortedDictionary<TKey, Tvalue> реализован как словарь, эти классы обладают разными характеристиками:

  • SortedList<TKey, TValue> использует меньше памяти, чем SortedDictionary<TKey, TValue>

  • SortedDictionary<TKey, TValue> быстрее вставляет и удаляет элементы.

  • При наполнении коллекции отсортированными данными SortedList<TKey,TValue> работает быстрее, если при этом не требуется изменение емкости.

В классе SortedDictionary<TKey, TValue> реализуются интерфейсы IDictionary, IDictionary<TKey, TValue>, ICollection, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable и IEnumerable<KeyValuePair<TKey, TValue>>. В классе SortedDictionary<TKey, TValue> предоставляются также следующие конструкторы:

public SortedDictionary()

public SortedDictionary(IDictionary<TKey, TValue> dictionary)

public SortedDictionary(IComparer<TKey> comparer)

public SortedDictionary(IDictionary<TKey, TValue> dictionary,

IComparer<TKey> comparer)

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

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

Add()

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

ContainsKey()

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

ContainsValue()

Возвращает логическое значение true, если вызывающий словарь содержит значение value, в противном случае — логическое значение false

Remove()

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

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

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

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

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

using System;

using System.Collections.Generic;

namespace ConsoleApplication1

{

class UserInfo

{

// Метод, реализующий словарь

public static SortedDictionary<string, string> MyDic(int i)

{

SortedDictionary<string, string> dic = new SortedDictionary<string, string>();

string s, s1;

for (int j = 0; j < i; j++)

{

Console.Write("\nВведите ключ: ");

s1 = Console.ReadLine();

Console.WriteLine("Введите имя сотрудника");

Console.Write("Name{0} --> ", j);

s = Console.ReadLine();

dic.Add(s1, s);

}

return dic;

}

}

class Program

{

static void Main()

{

Console.Write("Сколько сотрудников добавить? ");

try

{

int i = int.Parse(Console.ReadLine());

SortedDictionary<string, string> dic = UserInfo.MyDic(i);

// Получить коллекцию ключей

ICollection<string> keys = dic.Keys;

Console.WriteLine("\nСортированная база данных содержит: ");

foreach (string j in keys)

Console.WriteLine("ID -> {0} Name -> {1}", j, dic[j]);

}

catch (FormatException)

{

Console.WriteLine("Неверный ввод");

}

Console.ReadLine();

}

}

}

Работа программы:

Сколько сотрудников добавить? 4

Введите ключ: а

Введите имя сотрудника

Name0 --> Виктор

Введите ключ: я

Введите имя сотрудника

Name1 --> Алена

Введите ключ: п

Введите имя сотрудника

Name2 --> Дмитрий

Введите ключ: д

Введите имя сотрудника

Name3 --> Петро

Сортированная база данных содержит:

ID -> а Name -> Виктор

ID -> д Name -> Петро

ID -> п Name -> Дмитрий

ID -> я Name -> Алена

12.12Множества: классы HashSet<T> и SortedSet<T>

(доступно для MVS-2010)

Коллекция, содержащая только отличающиеся элементы, называется множеством (set). В составе .NET 4 имеются два множества — HashSet<T> и SortedSet<T>. Оба они реализуют интерфейс ISet<T>. Класс HashSet<T> содержит неупорядоченный список различающихся элементов, а в SortedSet<T> элементы упорядочены.

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

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

public HashSet ()

public HashSet(IEnumerable<T> collection)

public HashSet(IEqualityCompare comparer)

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

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

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

public IEqualityComparer<T> Comparer { get; }

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

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

public SortedSet()

public SortedSet(IEnumerable<T> collection)

public SortedSet(IComparer comparer)

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

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

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

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

public IComparer<T> Comparer { get; }

public T Max { get; }

public T Min { get; }

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

using System;

using System.Collections.Generic;

namespace ConsoleApplication1

{

class Program

{

static void Main()

{

// Создадим два множества

SortedSet<char> ss = new SortedSet<char>();

SortedSet<char> ss1 = new SortedSet<char>();

ss.Add('A');

ss.Add('B');

ss.Add('C');

ss.Add('Z');

ShowColl(ss, "Первая коллекция: ");

ss1.Add('X');

ss1.Add('Y');

ss1.Add('Z');

ShowColl(ss1, "Вторая коллекция");

ss.SymmetricExceptWith(ss1);

ShowColl(ss, "Исключили разноименность (одинаковые элементы) "+

" двух множеств: ");

ss.UnionWith(ss1);

ShowColl(ss, "Объединение множеств: ");

ss.ExceptWith(ss1);

ShowColl(ss, "Вычитание множеств");

Console.ReadLine();

}

static void ShowColl(SortedSet<char> ss, string s)

{

Console.WriteLine(s);

foreach (char ch in ss)

Console.Write(ch + " ");

Console.WriteLine("\n");

}

}

}