Dictionary對象將替換Collection對象,並提供附加的語言從而使增長和刪除記錄的速度比之前提升三倍算法
雖然Visual Basic 6.0只有不多的新特色,可是具備某些功能強大的新的對象模型,其中之一就是Dictionary對象。編程
Dictionary對象是無處不在的Visual Basic Collection對象的新版本。它的介紹存在於VBScript 2.0,並經過Visual Basic 6.0 對Scripting Runtime Library的支持涉入Visual Basic的所有內容。剛開始,Dictionary對象僅僅包含在VBScript中,並做爲Perl相關內容的等價體對Web組請求進行答覆。數組
與Collection對象類似,你可以經過Dictionary存儲任何類型的數據或字典對象,這些數據和對象一般被看做字典的組成部分,每一部分都被賦予字符串型鍵值。雖然我不認爲Microsoft意圖使你徹底擺脫收集和替換上述數據和對象的煩惱,可是實際上,在先前的Visual Basic 6.0文檔中,對Dcitionary對象確實不多說起,所以我認爲這是Visual Basic 6.0的一個最新的重要特色。瀏覽器
Dictionary對象與Collection對象的比較app
從Visual Basic 4.0開始,Collection對象就做爲主要的數據類型替代了用戶本身定義的類型,由此之後,大多數Visual Basic程序都包含Collection對象。若是你從Visual Basic 4.0開始已經很是習慣於使用Collection對象,那麼你又爲何須要做出改變呢?這主要有幾個因素:函數
Dictionary對象比Collection 對象更快,這種速度優點主要體如今增長數據成員、在字典中進行迭代搜索和刪除數據成員上。 性能
Dictionary對象包括那些你常常不得不本身編制的封裝函數,例如Exists函數和RemoveAll函數。測試
Dictionary對象讓你可以建立Key值數組和Item值數組,從而加快在字典中進行迭代搜索的速度。spa
Dictionary對象讓你可以覆蓋已經存在的Key值和已經存在的數據成員。 Dictionary對象還確實存在着下述缺點,但它們自己並不值一提:設計
與Collection對象不一樣,Dictionary對象不是VBA語言DLL的一部分,這意味着你須要藉助SCRRUN.DLL,並將之鏈接到相應的應用程序。
Dictionary對象實現For…Each…Next循環的方法也很奇怪,它不是返回Item值,而是返回Key值。
Dictionary對象還有一惱人之處,就是若是你想從字典中刪除一個沒有搜索到的成員,你就必須添加數據到這個空的條目或不存在的鍵。
訪問Dictionary對象正如我先前所說,Dictionary對象不是VBA或Visual Basic實時語言的具體存在的部分,它是存在於Microsoft Scripting Runtime Library(SCRRUN.DLL)中的一個對象。爲了在應用程序中使用Dictionary對象,就必須利用Reference對話框增長一個項目級的引用到Scripting Runtime Library。
增長完引用以後,建立Dictionary對象的實例,以下:
Dim oDict As Dictionary
Set oDict = New Dictionary
' Do some work.
Set oDict = Nothing
爲了增長一個成員到Dictionary對象,利用Add方法,其中包括兩個參數:須要增長的數據和與數據相關聯的字符串型Key值,語法以下:
dictionary.Add Key, Data
在Dictionary對象中沒有指明新的數據成員存放位置的參數,它將由字典本身挑出。你還須要注意Add方法的參數正好與Collection對象的Add方法相反,在Collection對象 中:
collection.Add Data, [Key], [before], [after]
與Collection對象相似,Dictionary對象的成員可以是任何數據類型、對象或其餘字典,從而使你可以按照本身的意願任意嵌套Dictionary對象。
訪問Dictionary對象的成員
Dictionary對象的Item方法是訪問包含在字典中數據的推薦方法,其好處是速度快,很是快。我所作的測試代表,訪問Dictionary對象數據成員的速度要比訪問Collection對象數據成員的速度快三倍。若是你打開對象瀏覽器,選擇Dictionary對象,並觀察隱藏的成員,你就會看到名爲HashVal的屬性,這代表Dictionary對象存在無用信息列表和一些奇怪的排隊算法。
在設計Dictionary對象時,主要是利用將字符串型Key值做爲一個參數傳遞給Item的方法來實現對數據的訪問,這一點與Collection對象類似,例如,你能夠利用:
VItem = oDictionary.Item(sKey)
這兒警告一句,若是試圖利用一個並不存在的鍵值返回Collection成員的數值,將會出錯(code 5, Invalid Procedure Call or Argument)。
Dictionary對象並不這樣,它在插入該新成員時,採用並不存在的鍵值對應某個鍵同時用零長度字符串對應數據成員。Dictionary對象老是檢查你要使用的鍵是否存在於字典內,能夠想象,這一特色可以輕易地捕捉不經意所犯的錯誤,至於檢查鍵值存在性的方法將在本文的後續內容中述及。
當使用Collection對象時,你不能直接順序地訪問字典中的數據,可是使用字典的Item方法時就不這樣,你可以快速地建立全部數據成員的數組,並利用該數組順序地訪問全部數據:
Dim vItems As Variant
Dim iOrdinal As Integer
iOrdinal = 10
vItems = oDictionay.Items
vItem = vItems(iOrdinal)
從Collection對象中刪除數據的方法一般是採用For…Each…Next語句,在你初次對Dictionary對象使用For…Each…Next時,能夠假設你從未對字典使用過該語句,可是儘管沒有當前的記錄位置,你仍可以使用For…Each…Next,你只須要Dictionary對象的inter_NewEnum函數返回的與條目有關的鍵值,而不是象Collection對象那樣,須要返回字典條目的索引,你能夠將這些鍵值傳遞給Item方法以便刪除數據成員,以下所示:
Dim sKey As Variant
For Each sKey in oDictionary
VItem = oDictionary.Item(sKey)
…
Next
當你在封裝類中利用Dictionary對象時,存在另外一個使用For…Each…Next的次要關鍵。你不能在客戶端使用For…Each…Next循環對數據成員進行迭代搜索,除非你願意進行大量的複雜編程。其緣由是Dictionary對象的internal_NewEnum函數不是一個隱含成員,而在Collection對象中它是,它不能經過Visual Basic調用,所以你不可以在封裝類實現本身的_NewEnum函數,簡單的Set NewEnum = mCol.[_NewEnum]語句不能與Dictionary對象共同工做,可是,使用Dcitionary對象得到的諸多好處使這種折中很是值得。
那麼,怎樣訪問Dictionary對象封裝類的每個成員呢?Dictionary對象包含名爲Items的方法,該方法返回全部Dictionary對象成員的一個可變數組,你只須要在本身的類中提供一個封裝子程序以返回Item數組:
Public Property Get Items() As Variant
Items = mdDict.Items
End Property
或者你願意提供一個更加有意義的名字給封裝特性,那麼能夠這樣:
Public Property Get Employees() As Variant
Employees = mdDict.Items
End Property
而後你的客戶端程序代碼就能夠利用For…Each…Next或For…Next循環在可變數組中進行迭代搜索,如下這些代碼告訴你怎樣才能實現這一點:
Dim oEmployees As Employees ' wrapper class
Dim aEmployees As Variant ' Variant to hold array
Dim oEmp As Employee ' data member class
Dim i As Integer ' simple counter
Set oEmployees = New Employees 'Dictionary wrapper class
aEmployees = oEmployees.Employees 'return an array of objects
For i = lBound(aEmployees) To uBound(aEmployees)
Set oEmp = aEmployees(i)
cboNames.AddItem oEmp.Name
Set oEmp = Nothing
Next i
Set oEmployees = Nothing
那麼性能怎樣呢?當在一樣的機器上調用動態鏈接庫時,結合Dictionary封裝類的Item數組和Foe…Each…Next的迭代搜索不如僅僅運用Collection封裝類進行的迭代搜索快,可是若是你處理的是遠程或進程外的服務程序,那麼狀況恰好相反。利用Dictionary的封裝類,你只是進行簡單數組的簡單轉換,而Collection類則反覆調用遠程服務程序,每個迭代都要進行過程調用。
我設置了一個簡單的實驗以考察遠程Dictionary對象和Collection對象的遷移性,這些對象包括1000個簡單的字符串成員並利用它們遷移一個客戶端Form的列表,Dictionary對象遷移該列表只須要四分之一秒,而Collection對象遷移該列表則耗費了差很少三秒鐘。
你的成員存在嗎?
我反覆抱怨Collection對象的一個因素是其沒有能力讓你預先知道Collection對象的某一個成員是否存在,若是該成員的鍵值並不存在,那麼你就不得不處理出現的錯誤。因爲這個緣由,我一般利用一個類來封裝個人Collection對象成員,並使它們包括Exists屬性。
無論怎樣,Microsoft使Dictionary對象具備Exists方法。Exists很是便於使用,並返回True或False,以下所示:
If oDictionary.Exsits(sKey) Then
' The key is there .
vVal = oDictionary.Item(sKey)
Else
MsgBox "The key doesn't exist"
End If
因爲Dictionary對象老是爲成員添加一個鍵值和一個空字符串,因此當你試圖返回一個並不存在鍵值的條目時,你老是可以在返回該條目以前利用Exists方法來檢測它的存在性(如上面例子所示),這個特色使你免於直接訪問一個並不存在的鍵值。
鍵值覆蓋
若是你曾經試圖改變某個與Collection對象成員對應的鍵值,那麼你知道這不可能。當對象成員加入到Collection對象時,該成員的數據和鍵值就已經被固定下來了。你能作的惟一選擇就是使用Remove方法清除該成員並增長一個新成員到該對象。可是,你可以利用Dictionary對象的Key特性來覆蓋該鍵的鍵值,以下例所示:
If oDictionary.Exists(sOldKey) Then
' The key is there .
oDictionary.Key(sOldKey) = sNewKey
Else
MsgBox "The key dosen't exsit"
End If
成員覆蓋
我猜測Microsoft在編制Collection對象時,他們假設Collection對象的成員一旦加入就再也不改變,他們爲何會認爲開發人員僅僅與靜態數據打交道呢?!所以,改變Collection對象成員的惟一辦法就是先從Collection對象中刪除它們並從新加入。
與Key特性類似,你可以利用存在於表達式兩邊的Dictionary對象的Item特性。在一個表達式的右邊,你返回對象成員的值,而在表達式的左邊,你能夠設置成員的值,方法以下:
If oDictionary.Exists(sKey) Then
' The key is there .
oDictionary.Item(sKey) = vNewItem
Else
MsgBox "The key doesn't exist"
End If
補充
當你須要字典內全部鍵值的數組時,Item方法和Key方法也可以幫助你。Item方法能夠返回包含字典內全部數據成員的可變數組,而Key方法則能夠返回包含字典內全部鍵值的可變數組。 Dictionary對象的其餘特性包括返回字典內成員數目的Count特性和可以讓你控制內部搜索執行狀況的CompareMode特性,還有Remove特性和RemoveAll特性,正如其名字所示,它們用於清除字典內的數據成員。
總結
Dictionary對象與Collection對象相比,是一個很是有價值的嘗試。它不但速度快,並且具備許多特性,使你從原來不得不本身編制封裝類的煩惱中解脫出來。雖然用Dictionary對象替換Collection對象還須要一些次要的記錄技術(根據For…Each…Next等而定),可是利用Dictionary對象所帶來的性能上的提升足以補償這些努力。本專題的PROFESSIONAL RESOURCE CD包含一
個例子類,從而向你展示怎樣圍繞Dictionary對象建立一個名爲DictCLass.CLS的封裝類,它還包括一個例子應用程序,該例子向你展現怎樣利用這些類來得到超出於你應用程序的強大功能。
Collection至關普及,大部分Visual Basic數據類都源於此類,而Dictionary對象是重要的改進,在添加和刪除對象成員方面要比Collection對象快三倍,你可以戲劇性地提升應用程序的性能。你也能夠本身進行Dictionary對象和Collection對象的性能測試比較,你會獲得與我大體相同的結果。
若是我能夠自由選擇(個人客戶有足夠的時間和金錢),那麼我將用Dictionary對象替換全部的Collection對象。這項工做尚未開始,至少本週不會進行,可是從如今開始,我用Visual Basic 編制的全部程序都將採用Dictionary對象。
Dictionary對象將替換Collection對象,並提供附加的語言從而使增長和刪除記錄的速度比之前提升三倍
雖然Visual Basic 6.0只有不多的新特色,可是具備某些功能強大的新的對象模型,其中之一就是Dictionary對象。
Dictionary對象是無處不在的Visual Basic Collection對象的新版本。它的介紹存在於VBScript 2.0,並經過Visual Basic 6.0 對Scripting Runtime Library的支持涉入Visual Basic的所有內容。剛開始,Dictionary對象僅僅包含在VBScript中,並做爲Perl相關內容的等價體對Web組請求進行答覆。
與Collection對象類似,你可以經過Dictionary存儲任何類型的數據或字典對象,這些數據和對象一般被看做字典的組成部分,每一部分都被賦予字符串型鍵值。雖然我不認爲Microsoft意圖使你徹底擺脫收集和替換上述數據和對象的煩惱,可是實際上,在先前的Visual Basic 6.0文檔中,對Dcitionary對象確實不多提及,所以我認爲這是Visual Basic 6.0的一個最新的重要特色。
Dictionary對象與Collection對象的比較
從Visual Basic 4.0開始,Collection對象就做爲主要的數據類型替代了用戶本身定義的類型,由此之後,大多數Visual Basic程序都包含Collection對象。若是你從Visual Basic 4.0開始已經很是習慣於使用Collection對象,那麼你又爲何須要做出改變呢?這主要有幾個因素:
Dictionary對象比Collection 對象更快,這種速度優點主要體現在增長數據成員、在字典中進行迭代搜索和刪除數據成員上。
Dictionary對象包括那些你常常不得不本身編制的封裝函數,例如Exists函數和RemoveAll函數。
Dictionary對象讓你可以建立Key值數組和Item值數組,從而加快在字典中進行迭代搜索的速度。
Dictionary對象讓你可以覆蓋已經存在的Key值和已經存在的數據成員。 Dictionary對象還確實存在着下述缺點,但它們自己並不值一提:
與Collection對象不一樣,Dictionary對象不是VBA語言DLL的一部分,這意味着你須要藉助SCRRUN.DLL,並將之鏈接到相應的應用程序。
Dictionary對象實現For…Each…Next循環的方法也很奇怪,它不是返回Item值,而是返回Key值。
Dictionary對象還有一惱人之處,就是若是你想從字典中刪除一個沒有搜索到的成員,你就必須添加數據到這個空的條目或不存在的鍵。
訪問Dictionary對象正如我先前所說,Dictionary對象不是VBA或Visual Basic實時語言的具體存在的部分,它是存在於Microsoft Scripting Runtime Library(SCRRUN.DLL)中的一個對象。爲了在應用程序中使用Dictionary對象,就必須利用Reference對話框增長一個項目級的引用到Scripting Runtime Library。
增長完引用以後,建立Dictionary對象的實例,以下:
Dim oDict As Dictionary
Set oDict = New Dictionary
' Do some work.
Set oDict = Nothing
爲了增長一個成員到Dictionary對象,利用Add方法,其中包括兩個參數:須要增長的數據和與數據相關聯的字符串型Key值,語法以下:
dictionary.Add Key, Data
在Dictionary對象中沒有指明新的數據成員存放位置的參數,它將由字典本身挑出。你還須要注意Add方法的參數正好與Collection對象的Add方法相反,在Collection對象 中:
collection.Add Data, [Key], [before], [after]
與Collection對象相似,Dictionary對象的成員可以是任何數據類型、對象或其餘字典,從而使你可以按照本身的意願任意嵌套Dictionary對象。
訪問Dictionary對象的成員
Dictionary對象的Item方法是訪問包含在字典中數據的推薦方法,其好處是速度快,很是快。我所作的測試代表,訪問Dictionary對象數據成員的速度要比訪問Collection對象數據成員的速度快三倍。若是你打開對象瀏覽器,選擇Dictionary對象,並觀察隱藏的成員,你就會看到名爲HashVal的屬性,這代表Dictionary對象存在無用信息列表和一些奇怪的排隊算法。
在設計Dictionary對象時,主要是利用將字符串型Key值做爲一個參數傳遞給Item的方法來實現對數據的訪問,這一點與Collection對象相似,例如,你能夠利用:
VItem = oDictionary.Item(sKey)
這兒警告一句,若是試圖利用一個並不存在的鍵值返回Collection成員的數值,將會出錯(code 5, Invalid Procedure Call or Argument)。
Dictionary對象並不這樣,它在插入該新成員時,採用並不存在的鍵值對應某個鍵同時用零長度字符串對應數據成員。Dictionary對象總是檢查你要使用的鍵是否存在於字典內,能夠想象,這一特色可以輕易地捕捉不經意所犯的錯誤,至於檢查鍵值存在性的方法將在本文的後續內容中述及。
當使用Collection對象時,你不能直接順序地訪問字典中的數據,但是使用字典的Item方法時就不這樣,你可以快速地建立全部數據成員的數組,並利用該數組順序地訪問全部數據:
Dim vItems As Variant
Dim iOrdinal As Integer
iOrdinal = 10
vItems = oDictionay.Items
vItem = vItems(iOrdinal)
從Collection對象中刪除數據的方法一般是採用For…Each…Next語句,在你初次對Dictionary對象使用For…Each…Next時,能夠假設你從未對字典使用過該語句,可是儘管沒有當前的記錄位置,你仍可以使用For…Each…Next,你只須要Dictionary對象的inter_NewEnum函數返回的與條目有關的鍵值,而不是象Collection對象那樣,須要返回字典條目的索引,你能夠將這些鍵值傳遞給Item方法以便刪除數據成員,以下所示:
Dim sKey As Variant
For Each sKey in oDictionary
VItem = oDictionary.Item(sKey)
…
Next
當你在封裝類中利用Dictionary對象時,存在另外一個使用For…Each…Next的次要關鍵。你不能在客戶端使用For…Each…Next循環對數據成員進行迭代搜索,除非你願意進行大量的複雜編程。其緣由是Dictionary對象的internal_NewEnum函數不是一個隱含成員,而在Collection對象中它是,它不能經過Visual Basic調用,所以你不可以在封裝類實現本身的_NewEnum函數,簡單的Set NewEnum = mCol.[_NewEnum]語句不能與Dictionary對象共同工做,可是,使用Dcitionary對象得到的諸多好處使這種折中很是值得。
那麼,怎樣訪問Dictionary對象封裝類的每個成員呢?Dictionary對象包含名爲Items的方法,該方法返回全部Dictionary對象成員的一個可變數組,你只須要在本身的類中提供一個封裝子程序以返回Item數組:
Public Property Get Items() As Variant
Items = mdDict.Items
End Property
或者你願意提供一個更加有意義的名字給封裝特性,那麼能夠這樣:
Public Property Get Employees() As Variant
Employees = mdDict.Items
End Property
而後你的客戶端程序代碼就能夠利用For…Each…Next或For…Next循環在可變數組中進行迭代搜索,如下這些代碼告訴你怎樣才能實現這一點:
Dim oEmployees As Employees ' wrapper class
Dim aEmployees As Variant ' Variant to hold array
Dim oEmp As Employee ' data member class
Dim i As Integer ' simple counter
Set oEmployees = New Employees 'Dictionary wrapper class
aEmployees = oEmployees.Employees 'return an array of objects
For i = lBound(aEmployees) To uBound(aEmployees)
Set oEmp = aEmployees(i)
cboNames.AddItem oEmp.Name
Set oEmp = Nothing
Next i
Set oEmployees = Nothing
那麼性能怎樣呢?當在一樣的機器上調用動態鏈接庫時,結合Dictionary封裝類的Item數組和Foe…Each…Next的迭代搜索不如僅僅運用Collection封裝類進行的迭代搜索快,可是若是你處理的是遠程或進程外的服務程序,那麼狀況恰好相反。利用Dictionary的封裝類,你只是進行簡單數組的簡單轉換,而Collection類則反覆調用遠程服務程序,每個迭代都要進行過程調用。
我設置了一個簡單的實驗以考察遠程Dictionary對象和Collection對象的遷移性,這些對象包括1000個簡單的字符串成員並利用它們遷移一個客戶端Form的列表,Dictionary對象遷移該列表只須要四分之一秒,而Collection對象遷移該列表則耗費了差很少三秒鐘。
你的成員存在嗎?
我反覆抱怨Collection對象的一個因素是其沒有能力讓你預先知道Collection對象的某一個成員是否存在,若是該成員的鍵值並不存在,那麼你就不得不處理出現的錯誤。因爲這個緣由,我一般利用一個類來封裝個人Collection對象成員,並使它們包括Exists屬性。
無論怎樣,Microsoft使Dictionary對象具備Exists方法。Exists很是便於使用,並返回True或False,以下所示:
If oDictionary.Exsits(sKey) Then
' The key is there .
vVal = oDictionary.Item(sKey)
Else
MsgBox "The key doesn't exist"
End If
因爲Dictionary對象老是爲成員添加一個鍵值和一個空字符串,因此當你試圖返回一個並不存在鍵值的條目時,你老是可以在返回該條目以前利用Exists方法來檢測它的存在性(如上面例子所示),這個特色使你免於直接訪問一個並不存在的鍵值。
鍵值覆蓋
若是你曾經試圖改變某個與Collection對象成員對應的鍵值,那麼你知道這不可能。當對象成員加入到Collection對象時,該成員的數據和鍵值就已經被固定下來了。你能作的惟一選擇就是使用Remove方法清除該成員並增長一個新成員到該對象。可是,你可以利用Dictionary對象的Key特性來覆蓋該鍵的鍵值,以下例所示:
If oDictionary.Exists(sOldKey) Then
' The key is there .
oDictionary.Key(sOldKey) = sNewKey
Else
MsgBox "The key dosen't exsit"
End If
成員覆蓋
我猜測Microsoft在編制Collection對象時,他們假設Collection對象的成員一旦加入就再也不改變,他們爲何會認爲開發人員僅僅與靜態數據打交道呢?!所以,改變Collection對象成員的惟一辦法就是先從Collection對象中刪除它們並從新加入。
與Key特性類似,你可以利用存在於表達式兩邊的Dictionary對象的Item特性。在一個表達式的右邊,你返回對象成員的值,而在表達式的左邊,你能夠設置成員的值,方法以下:
If oDictionary.Exists(sKey) Then
' The key is there .
oDictionary.Item(sKey) = vNewItem
Else
MsgBox "The key doesn't exist"
End If
補充
當你須要字典內全部鍵值的數組時,Item方法和Key方法也可以幫助你。Item方法能夠返回包含字典內全部數據成員的可變數組,而Key方法則能夠返回包含字典內全部鍵值的可變數組。 Dictionary對象的其餘特性包括返回字典內成員數目的Count特性和可以讓你控制內部搜索執行狀況的CompareMode特性,還有Remove特性和RemoveAll特性,正如其名字所示,它們用於清除字典內的數據成員。
總結
Dictionary對象與Collection對象相比,是一個很是有價值的嘗試。它不但速度快,並且具備許多特性,使你從原來不得不本身編制封裝類的煩惱中解脫出來。雖然用Dictionary對象替換Collection對象還須要一些次要的記錄技術(根據For…Each…Next等而定),可是利用Dictionary對象所帶來的性能上的提升足以補償這些努力。本專題的PROFESSIONAL RESOURCE CD包含一
個例子類,從而向你展示怎樣圍繞Dictionary對象建立一個名爲DictCLass.CLS的封裝類,它還包括一個例子應用程序,該例子向你展現怎樣利用這些類來得到超出於你應用程序的強大功能。
Collection至關普及,大部分Visual Basic數據類都源於此類,而Dictionary對象是重要的改進,在添加和刪除對象成員方面要比Collection對象快三倍,你可以戲劇性地提升應用程序的性能。你也能夠本身進行Dictionary對象和Collection對象的性能測試比較,你會獲得與我大體相同的結果。
若是我能夠自由選擇(個人客戶有足夠的時間和金錢),那麼我將用Dictionary對象替換全部的Collection對象。這項工做尚未開始,至少本週不會進行,可是從如今開始,我用Visual Basic 編制的全部程序都將採用Dictionary對象。