HashTable的實現

 

爲了實現高效訪問一個集合中的元素,Mpr中實現了Hash Table,其結構如下圖所示

圖片

 

類的簡單描述:

MprHashTable是存儲和管理Hash內容的類,MprHashEntry是HashTable的一項,有三個派生類MprStringHashEntry、MprStaticHashEntry和MprObjectHashEntry

 

個各類的詳細描述:

MprHashTable類

如下圖所示,MprHashTable類使用鏈表來存儲存在衝突的元素集,但是這只是MprHashTable類的簡單結構

圖片

成員變量

   MprList   *buckets;
   int    size;
   int    count;

正如我在MprList實現那章所描述的一樣,類MprList在這裏的目的是用於存儲MprLink(或其子類但不包括MprList)類型的指針,在這裏是MprHashEntry或其子類MprStringHashEntry、MprStaticHashEntry和MprObjectHashEntry

成員函數

插入操作:int MprHashTable::insert(MprHashEntry *value)

實例:MprHashTable hashTable, MprHashEntry* entryPtr1, MprHashEntry* entryPtr2,entryPtr1和entryPtr2指向兩個MprHashEntry的實例

1.在執行插入操作之前

圖片

2.entryPtr1 = new MprHashEntry(“item1”);

    hashTable.insert(entryPtr1);

在這步操作之後,hashTable所做的事情是通過調用lookupInner查看entryPtr1是否在hashTable中,並返回buckets[i]的地址給bp和"item1"在bp指向的鏈表中的位置給ep,如果ep不等於NUll,則將entryPtr1插入到ep之後,然後刪除ep。否則將entryPtr1插入到對應項。假設"item1"對應的hash值爲0,即i = 0。
圖片

3.entryPtr2 = new MprHashEntry(“item2”);

    hashTable.insert(entryPtr2);

3.1如果"item2"的hash值和"item1"的hash值不同,假設這時候的hash值爲2,即i = 2

圖片

3.2如果"item2"的hash值和"item1"的hash值相同

圖片

3.3 再一次執行如下操作

 entryPtr3 = new MprHashEntry(“item2”);

    hashTable.insert(entryPtr3);

圖片

在插入entryPtr3之前,首先通過通過hash函數計算出"Item2"對應的hash值,然後將entryPtr3插入到entryPtr2的後面,最後刪除entryPtr2

 

 MprList   *bp;
 MprHashEntry  *ep;

 if ((ep = lookupInner(value->key, &bp)) != 0) {
    //
    // Already exists. Delete the old and insert the new. Insert
    // first to make sure we get the right position
    //
    ep->insertAfter(value);
    value->bucket = bp;
    bp->remove(ep);
    delete ep;
    return 0;
 }
 //
 // New entry
 //
 bp->insert(value);
 value->bucket = bp;
 count++;
 return 0;

 

刪除操作1:int MprHashTable::remove(char *key)
通過lookupInner函數查找key對應的bucket[i]和該鏈表上的對應item

 MprList   *bp;
 MprHashEntry  *ep;

 if ((ep = lookupInner(key, &bp)) == 0) {
    return MPR_ERR_NOT_FOUND;
 }
 bp->remove(ep);
 count--;
 return 0;

 

刪除操作2:int MprHashTable::remove(MprHashEntry *ep)

 ep->bucket->remove(ep);
 count--;
 return 0;

 

刪除hashTable中的所有元素:int MprHashTable::removeAll()

依次遍歷所有的bucket[i],刪除該隊列中所有的元素

 

查找:MprHashEntry *MprHashTable::lookup(char *key)

   MprHashEntry *MprHashTable::lookupInner(char *key, MprList **bucket)

在這裏我們只講第二個函數,該函數的操作過程是:首先通過通過hash函數將key值映射到i上,通過buckets[i]查找到對應的MprList的入口點,再依次查找MprList,將其元素的key值和目標元素的key值進行比較,直到找到對應的item,則返回該item;否則返回NULL。

 MprList   *bp;
 MprHashEntry *ep;
 int    index, rc;

 mprAssert(key);

 index = hashIndex(key);
 bp = &buckets[index];
 ep = (MprHashEntry*) bp->getFirst();
 if (bucket) {
    *bucket = bp;
 }

 while (ep) {
    rc = strcmp(ep->key, key);
    if (rc == 0) {
       return ep;
    }
    ep = (MprHashEntry*) bp->getNext(ep);
 }
 return 0;

得到第一個元素:MprHashEntry *MprHashTable::getFirst()

從buckets[0]由左向右,由上向下查找,如果找到有一個鏈表中的元素不爲NULL,則返回


得到下一個元素:MprHashEntry *MprHashTable::getNext(MprHashEntry *ep)
從ep開始由左向右,由上向下查找,如果找到有一個鏈表中的元素不爲NULL,則返回

 

hash映射:

int MprHashTable::hashIndex(char *key)
{
   uint  sum;

   sum = 0;
   while (*key) {
      sum += (sum * 33) + *key++;
   }

   return sum % size;
}

這個函數的選取特別重要,因爲一個好的hash函數可以減少衝突,使各個bucket內的內容比較均勻。

 

MprHashEntry、MprStringHashEntry、MprStaticHashEntry和MprObjectHashEntry這四個類都比較簡單,這裏就不描述了,後兩個類是從MprHashEntry繼承,他們和MprHashEntry的不同時由於他們除了有key值還有對應的value值。其中MprStringHashEntry和MprStaticHashEntry的區別是MprStringHashEntry中的value是一個可以修改的MprStr類型,而MprStaticHashEntry中的value是char*類型,我們知道該類型指向的指針式不能夠被修改的。而MprObjectHashEntry是定義在編譯宏PERHAPS下面的,其定義和MprStaticHashEntry是完全一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章