Хэш-таблицы
Простые и динамические массивы удобны прежде всего тем, что вы можете напрямую обратиться к любому элементу по индексу. Конечно, для этого необходимо знать индекс. В следующей структуре данных — хэш-таблице — произвольный доступ к данным осуществляется по ключу. Допустим, у вас имеется хэш-таблица с именем
theData. Команда theData("Bill 's Address") позволяет извлечь из хэш-таблицы нужный элемент без циклического перебора всего содержимого. Хэш-таблицы очень удобны в ситуациях, когда вы хотите получить быстрый доступ к значению по связанному с ним уникальному атрибуту, то есть ключу. Разумеется, программирование хэш-таблицы — задача непростая [ Для этого необходимо построить хорошую функцию хэширования для вычисления индекса данных по ключу, а также решить неизбежную проблему коллизий, то есть совпадения хэш-кодов у двух разных элементов. Даже терминология выглядит устрашающе... ], но, к счастью, эта работа уже выполнена за вас разработчиками .NET Framework.
В табл. 4.4 перечислены важнейшие методы класса Hashtable (за полным списком обращайтесь к электронной документации).
Таблица
4.4. Важнейшие методы класса Hashtable
Имя |
Описание |
||
Add |
Добавляет новую
пару «ключ/значение» в хэш-таблицу |
||
Clear |
Удаляет из хэш-таблицы
все содержимое |
||
ContainsKey |
Проверяет, содержит
ли хэш-таблица заданный ключ (с учетом регистра символов) |
||
ContainsValue
СоруТо |
Проверяет, содержит
ли хэш-таблица заданное значение (с учетом регистра символов) Копирует элементы
хэш-таблицы в массив |
||
Count |
Возвращает количество
пар «ключ/значение» в хэш-таблице |
||
Item |
Свойство по умолчанию. Получает или задает значение, связанное с указанным ключом |
||
Keys |
Возвращает все
ключи хэш-таблицы в виде коллекции, содержимое которой перебирается
в цикле For-Each |
||
Remove |
Удаляет из хэш-таблицы
значение с заданным ключом |
||
Values |
Возвращает все
значения хэш-таблицы в виде коллекции, содержимое которой перебирается
в цикле For-Each |
||
1 Option Strict On
2 Imports System.Environment
3 Module Modulel
4 Sub Main()
5 Dim eVariables As Hashtable
6 eVariables =CType(GetEnvironmentVariables(). Hashtable)
7 Console.Writel_ine("Press Enter to see the next item")
8 Dim thing As Object
9 For Each thing In eVariables.Keys
10 Console.WriteLineC'The environment variable named " & _
11 thing. ToString() & "has value " & eVariables(thing).ToString())
12 Console. ReadLine()
13 Next
14 End Sub
15 End Module
Прежде всего использованный в строке 6 упрощенный синтаксис имени метода стал возможным благодаря вызову Imports в строке 2: eVariables =CType(GetEnvironmentVariables(),Hashtable)
Значение, полученное при вызове GetEnvironmentVariables(), преобразуется в хэш-таблицу функцией СТуре [ Возможно, в будущих иерсиях .NET такое преобразование работать не будет. ]. В строках 8 и 9 для перебора элементов хэш-таблицы используется переменная типа Object:
Dim thing As Object
For Each thing In eVariables.Keys
В стандартных хэш-таблицах хранятся только объекты. Но поскольку в VB .NET все данные являются объектными, строковые значения переменных окружения также могут сохраняться в переменной thing. Программа перебирает содержимое коллекции Keys и при помощи свойства Item для каждого ключа получает ассоциированное значение. Конструкцию eVariables(thing) в строке 11 также можно записать в следующем виде: eVariables.Item(thing)
В строке 11 вызывается метод ToString, определенный в каждом классе (этот важный метод описан в главе 5). Здесь этот метод используется для вывода строкового представления ключа.
Рассмотрим следующий фрагмент:
Dim thing As New Object
Dim aRandomlnstance As New Random
В нем объявляются и создаются две переменные: thing и aRandomlnstance. Первая переменная содержит ссылку на тип Object, а вторая — ссылку на экземпляр класса Random. Следующая команда вполне допустима даже в режиме жесткой проверки типов (Option Strict On), поскольку в VB .NET все переменные в конечном счете представляют собой объекты: thing = aRandomlnstance
С другой стороны, обратное присваивание (aRandomlnstance = thing) недопустимо, поскольку не каждый объект является экземпляром класса Random.
Объектную переменную можно рассматривать как манипулятор блока памяти (причем не фиксированного, а перемещаемого). Объектные переменные также часто называют ссылками (references) или интеллектуальными указателями (smart pointers). Обычно при использовании знака = с ключевым словом New манипулятор связывается с блоком памяти, в котором хранится соответствующий объект (при работе с так называемыми структурными типами возникают некоторые тонкости, которые будут рассматриваться далее в этой главе).
Если объектная переменная содержит манипулятор блока памяти, в результате операции присваивания второй объектной переменной будет присвоен манипулятор того же блока памяти. Но если вы забудете о том, что для работы с одним блоком памяти используются две разные переменные, это может привести к печальным последствиям — изменения в состоянии объекта, внесенные через одну переменную, автоматически повлияют на другую переменную. Для примера рассмотрим следующий фрагмент:
Sub Maln()
Dim A As New ArrayList()
Dim В As ArrayList
В = А
B.Add("foo")
Console.WriteLine(A.Count)
Console.ReadLine() End Sub
Динамический массив А также будет содержать строку foo, поэтому выведенное значение A.Count будет равно 1.
Поскольку в VB .NET строки и массивы являются объектами, следует помнить, что для работы с ними используются объектные переменные. Как было показано в главе 3, это позволяет использовать встроенные возможности соответствующих классов при помощи синтаксиса «.». Например, при работе с массивом через переменную апАггау команда anArray.Sort() отсортирует массив чрезвычайно эффективным методом быстрой сортировки.
Как и в прежних версиях VB, объектные переменные могут использоваться для получения более компактной записи. Например, в следующем фрагменте определяется короткое имя аВох, которое будет использоваться вместо длинного Му-
Form.TextBoxl:
Dim aBox As System.Windows.Forms.TextBox aBox = MyForm.TextBoxl
Подобные сокращения часто используются в сочетании с ключевым словом With:
With aBox
.AutoSize =False
.Height =1000
.Width =200
.Text ="Hello" End With
Оператор Is проверяет, ссылаются ли две объектные переменные на одну область памяти. Следующий фрагмент в обоих случаях выводит True, поскольку в результате операций присваивания все объектные переменные ссылаются на одну область памяти:
Dim Objectl As New Object()
Dim ObjectZ As New Object()
Dim Objects As New Object()
ObjectZ =Object1
Objects Object2
Console.WriteLine(Objectl Is Object2)
Console.WriteLine(Object1 Is Object3)
Как и в прежних версиях VB, присваивание объектной переменной значения Nothi ng разрывает ее связь с блоком памяти. Когда объектная переменная равна Nothing, она не ассоциируется ни с каким объектом. В этом состоянии находятся все объектные переменные, которые были объявлены в программе, но еще не инициализировались. В программе часто встречаются проверки следующего вида:
If anObject Is Nothing Then
' Переменная не связана с объектом, присвоить значение Else
' Значение было присвоено ранее End If
Дополнительная информация о том, что происходит при присваивании объектным переменным значения Nothing, приведена в разделе «Сборка мусора и завершение».
Переменные, объявленные с типом Object, могут использоваться для хранения произвольных объектов. Следовательно, программисту необходимы средства для определения типа объекта, связанного с объектной переменной. В VB .NET эта задача решается двумя способами: функцией TypeName и оператором TypeOf ...Is.
Функция TypeName возвращает строку с описанием типа. Для всех типов, кроме базовых, должен быть предварительно вызван оператор New; в противном случае функция возвращает строку Nothing. Например, следующий фрагмент выводит в консольном окне строку Nothing:
Dim anSBuilder As System.Text.StringBuilder
Console.WriteLineC'My type name is " & TypeName(anSBuilder))
Но после вызова New в окне будет выведена строка StringBuilder:
Dim anSBuilder As New System.Text.StringBuilder
Console.WriteLineC'My type name is " & TypeName(anSBuilder))
Функция TypeName возвращает короткое имя класса, поэтому не рассчитывайте получить полное имя вида System.Text.StringBuilder.
Если вызвать функцию TypeName для массива, вы получите строковое имя, за которым следует пустая пара круглых скобок. Пример:
Dim aThing(5)As Integer
Console.WriteLine("My type Harness " & TypeName(aThing))
Полученная строка имеет вид Integer().
Функция TypeName удобна в процессе отладки, но в окончательных версиях программ обычно используется оператор TypeOf...Is. Он работает гораздо эффективнее, поскольку обходится без сравнений строк, необходимых при использовании TypeName. Синтаксис проверки выглядит следующим образом:
If TypeOf aThing Is System.Text.SthngBuilder Then
' Объект относится к типу StringBuilder End If
Проблемы с передачей объектных переменных по значению
Большинство программистов руководствуется простым правилом: если параметр передавался по ссылке, его изменения сохраняются в исходной переменной, а если по значению — изменения теряются после выхода из функции или процедуры. К сожалению, в случае с объектными переменными это правило не всегда истинно. Попробуйте выполнить следующий фрагмент, в котором массив передается в процедуру по значению. Вы убедитесь в том, что исходный массив изменяется после вызова процедуры!
Module Modulel Sub Main()
Dim a() As String ={"HELLO"."GOODBYE"}
Console.WriteLineC'Original first item in array is:" & a(0))
Console.WriteLineC'Original second item in array is:" & a(1))
Yikes(a) ' Массив передается по значению!
Console.WriteLineC'After passing by value first item in array now is:"_
&A(0))
Console.WriteLine("After passing by value second item in array is:"_
&АШ)
Console. ReadLine() End Sub Sub Yikes(ByVal Foo As String())
Foo(0) = "GOODBYE"
Food) = "HELLO" End Sub End Module
Рис.
4.7. Результат работы тестовой программы
Главная причина заключается в том, что при передаче по значению всегда создается новая копия исходной переменной; после выхода из функции эта копия уничтожается. Но, передавая по значению объектную переменную, вы приказываете VB .NET создать копию манипулятора для работы с объектом. Внутри процедуры операции с временным манипулятором отражаются на содержимом этой области памяти. После вызова из процедуры копия уничтожается, но все изменения в содержимом памяти остаются в силе.
Представьте себе чемодан, к которому временно приделали вторую ручку. Вы перенесли чемодан за новую ручку на другое место; даже если теперь отсоединить ручку, чемодан все равно останется на новом месте.
В этой странной ситуации есть лишь одно исключение — когда исходный объект является неизменяемым (immutable). Из стандартных, постоянно используемых классов к этой категории относится только класс Stri ng. В этом случае передача по значению работает именно так, как положено, в чем нетрудно убедиться при помощи следующей программы:
Option Strict On Module Modulel Sub Main()
Dim A As String = "hello"
NoProblem(A)
Console.WriteLine("After passing by value the string is still " & A)
Console. ReadLine() End Sub
Sub NoProblem(ByVal Foo As String)
Foo = "goodbye" End Sub End Module
Назад | Начало | Вперед |