HashTable的实现

来源:互联网 发布:软件配置管理流程 编辑:程序博客网 时间:2024/05/21 10:50

 

为了实现高效访问一个集合中的元素,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是完全一样。

原创粉丝点击