字典與集合(Dictionary與Collection)


    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對象。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM