`

C语言实现HashTable

阅读更多

C语言的少即是多:

 

从语言内容来讲,C绝对是足够精炼的,它提供且仅提供了我们工作所必须的编程元素。

从可以实现的功能以及能为我们提供的代码管理和性能支持上来看,它也做的恰到好处。

没有C++的繁琐、比脚本及所谓的OO语言更高效、当然也比汇编更容易理解。

 

不过对于用惯了Java的HashMap、LinkedHashMap,Python的Dict,以及PHP的Array 的同学来说,C的简洁似乎就有些简陋,甚至是蹩脚了。

 

最近项目中需要在C语言中使用HashTable来提高按键的查找速度,在网上找了很多现成的实现,发现写的都很随意,都有问题。很多现有的实现版本中都是使用char*作为key,void*作为值,这种做法最简单,但内存效率和计算效率都不高。

这种方案面临一个问题,就是:HashTable是否要申请新的内存空间来保存key和value的值,而不是只记录指针的值。

如果不保存,则指针指向的内存区可能会被其他代码销毁,则内容会丢失,程序会失败。

如果保存,就需要进行频繁的内存申请和销毁,尤其是当键或值是语言内置的基础类型(比如char、int、long、short、float、double)时,存放值的内存大小比存放指针值的内存还小。会导致更多的小块内存的操作和内存碎片。

为了能够让C更好地为我们的工作服务,我决定自己搞一个HashTable。

 

具体需求:

  1. 在我的项目中,很多情况下HashTable的key和value都是内置基础类型(如int、double),字符串的情况也比较多,其他的复杂情况极少。即我们的HashTable更多的是处理内置类型数据或者字符串数据。
  2. 需要支持对key、value按照插入顺序进行遍历。

我们的方案:

  1. 考虑到内置类型的size最大只有8Byte,而且所有指针本身的大小也是8Byte(64bit 的机器),因此我们只需要一个8Byte的空间来存错所有的基础类型的值,或者指针(一般是char*)。这样当面对基础类型的时候,不需要malloc额外的空间来存储,在遇到字符串类型(char*)的数据时,使用malloc申请内存空间存储字符串内容,并将指针存在这个8Byte的空间中。
  2. 同时HashTable要维护当前key和value的类型是什么,需要在插入数据和查找数据时根据key和value的类型做对应的类型转换。
  3. key和value都支持有限的类型:key的类型只支持int、long、char*;value的类型支持char、short、int、long、float、double、char*。
  4. 至于按照插入顺序进行遍历,则只需要对插入的每个元素维护一个全局的指针域即可,这个可以参考Java中LinkedHashMap的实现。

数据结构设计:

 

考虑到上述情况,我们对HashTable的结构设计如下:

 

#define VLEN       8
#define TNLEN     32


typedef unsigned long ulong;
typedef unsigned int  uint;

typedef struct _bucket {
    ulong h;          /* hash value of key, keyvalue if key is a uint or ulong */
    char * key;       /* the point to key , if key is a string */
    char value[VLEN]; /* store a var of builtin type in a 8Byte buffer */
    struct _bucket *pListNext;
    struct _bucket *pListLast;
    struct _bucket *pNext;
    struct _bucket *pLast;
} Bucket;

typedef struct _hashtable{
    int nTableSize;
    int nTableMask;
    int nNumOfElements;
    char keyType[TNLEN];     /* can be "int","long","char*" */
    char valueType[TNLEN];   /* can be "char","short","int","long","float","double","char*" */
    Bucket * pInternalPointer;
    Bucket * pListHead;
    Bucket * pListTail;
    Bucket ** arBuckets;
} HashTable;
 

  

内存结构:

 

假设我们创建一个size(桶数)为6的HashTable,并且尝试插入4个元素,其中第一个元素和第四个元素hash冲突,第二个元素与第三个元素hash冲突。那么按照设计,该HashTable在内存中的结构如下图所示:
 
在按键查找时,先通过计算hash值,并计算hash值对应的桶的索引[0,6),然后按照蓝色箭头pNext(指针)的指向即可找到对应的元素(或者找不到)。
在按照插入顺序遍历时,从head指针开始,按照墨色箭头pListNext(指针) 的指向即可完成元素的遍历。
 
接口需求:
我们希望这个HashTable能够支持多种数据类型,而且在使用的时候尽可能的方便。
用户在创建HashTable的实例时指定key和value的类型,在进行增、删、改、查以及遍历操作时直接使用对应的类型操作即可。
假设用户系统通过如下方式访问该HashTable:
/*创建HashTable实例*/
HashTable * ht = create_hashtable(100,char*,double);  /*key:char*,value:double*/

/*插入元素"xiaoqiang" => 1234.567 */
hash_add("xiaoqiang",1234.567); 

/*插入元素"helloworld" => 234567.891 */ 
hash_add("helloworld",234567.891);  

/*遍历元素*/ 
char * key = NULL;  
double value = 0.0;  
for (reset(ht);isnotend(ht);next(ht)){   
    key = skey(ht);              /*获取当前字符串key*/  
    value = *(double*)value(ht); /*获取当前double类型的value值,需要做类型转换*/  
    printf("key: %s, value:%lf\n",key,value);  
}
 

接口设计:

 

为了向用户提供上述访问HashTable内容的方式,我们对HashTable的访问接口设计如下:

 

 

#define create_hashtable(size, ...)           \
       _create_hashtable(size, #__VA_ARGS__)

#define hash_add(ht,key,value)                \
       _hash_add((ht),(key),(value))

#define hash_find(ht,key,value)               \
       _hash_find((ht),(key),(value))

#define hash_del(ht,key)                      \
       _hash_del((ht),(key))

#define hash_exists(ht,key)                   \
       _hash_exists((ht),(key))

#define reset(ht)       ((ht)->pInternalPointer = (ht)->pListHead)
#define next(ht)        ((ht)->pInternalPointer = (ht)->pInternalPointer->pListNext)
#define isnotend(ht)    ((ht)->pInternalPointer != NULL)
#define nkey(ht)        ((ht)->pInternalPointer->h)
#define skey(ht)        ((ht)->pInternalPointer->key)
#define value(ht)       ((ht)->pInternalPointer->value)


HashTable * _create_hashtable(uint size, const char* s_typename);
int _hash_add(HashTable * ht, ...);
int _hash_find(HashTable * ht, ...);
int _hash_del(HashTable * ht, ...);
int _hash_exists(HashTable * ht, ...);
int  hash_num_elements(HashTable * ht);
void hash_free(HashTable * ht);

 

 

上述结构设计和接口设计共同构成了我们HashTable的头文件hashtable.h

 

剩下的就是实现_create_hashtable、_hash_add、_hash_add、_hash_find、_hash_del、_hash_exists、hash_num_elements和hash_free函数了。

 

具体的实现细节和更多的测试用例,就不在此一一列出。

用户可以访问该项目在googlecode上的地址:https://code.google.com/p/c-hash/

里面有完整的项目代码,并提供动态库libht.so,及静态库libhts.a 的库文件.

 

最后,欢迎拍砖和提出各种改进意见。

  • 大小: 33.3 KB
分享到:
评论
3 楼 liuzhiqiangruc 2015-12-16  
zhoumengkang 写道
不就是 PHP 的实现么?

就是参照PHP的实现做的简化。
2 楼 zhoumengkang 2015-06-25  
哈哈~谢谢博主!有收获!
1 楼 zhoumengkang 2015-06-25  
不就是 PHP 的实现么?

相关推荐

    c语言实现hashtable,一键多值

    c语言实现hashtable,一个key可以对应多个data,c语言实现

    利用C语言实现HashTable

    本篇文章将详细探讨如何利用C语言实现一个简单的哈希表。 首先,我们需要定义哈希表的访问接口。`hashtable_new` 函数用于创建一个新的哈希表,传入参数`size`表示哈希表的大小,即预分配的节点数量。`hashtable_...

    C语言hashtable

    标题中的"C语言hashtable"指的是使用C语言编写的哈希表实现。在C/C++中,我们通常需要自己设计哈希表的结构并实现相关的哈希函数、冲突解决策略以及插入、删除、查找等操作。这个描述暗示了提供的.c文件包含了一个...

    c语言 hashtable

    在C语言中,虽然没有内置的哈希表库,但我们可以自定义实现一个高效的哈希表。 哈希表的核心概念: 1. **散列函数**:哈希表的效率很大程度上取决于散列函数的选择。一个好的散列函数应该能够将键均匀地分布在整个...

    c语言实现的hashtable分享

    }int hash_remove(HashTable *ht, char *key) { int index = hash_index(ht, key); Bucket *bucket = &ht->buckets[index]; Bucket *prev = NULL; while (NULL != bucket) { if (strcmp(key, bucket->key) == 0) { ...

    C语言 实现 车辆罚单管理系统

    在C语言中实现这样的系统,通常涉及到以下几个关键知识点: 1. **数据结构**:车辆罚单信息可能包括车牌号、驾驶员姓名、违章日期、违章类型、罚款金额等。这些数据可以通过结构体(struct)来封装,创建一个代表...

    用C语言实现一个简单的哈希表(hash table)

    - 在这个C语言实现中,哈希函数可能是一个简单的模运算,例如`hash(key) = key % size`,其中`size`是哈希表的大小。 3. **冲突解决:链地址法**: - 链地址法是为每个数组元素创建一个链表,当新键映射到已有的...

    哈希表的建立与运用C语言实现哈希表的建立与运用C语言实现

    以上就是使用C语言实现哈希表的基本流程和关键操作。实际应用中,还需要考虑负载因子、动态扩容等问题,以确保哈希表的性能。通过理解和实践这些知识点,可以深入掌握哈希表的工作原理,提高程序的效率。

    HASH 索引——用C语言实现

    下面是一个简化的C语言实现框架: ```c typedef struct Node { char key[KEY_SIZE]; int value; struct Node* next; } Node; typedef struct HashTable { int size; Node** buckets; } HashTable; HashTable...

    哈希表-使用C语言实现哈希表数据结构-HashTable.zip

    "哈希表_使用C语言实现哈希表数据结构_HashTable"这个压缩包很可能包含了具体的C语言代码示例,通过阅读和理解这些代码,你可以深入学习哈希表的实现细节,包括如何定义结构体、如何定义和应用哈希函数、如何处理...

    哈希表hashtable实现

    7. C语言实现:在C语言中,哈希表可以使用结构体来表示,结构体内包含一个指向数组的指针,以及数组的大小等信息。同时,需要提供相应的API,如`insert`、`search`、`delete`等,用于操作哈希表。 8. 效率与性能:...

    哈希表的c语言实现1

    哈希表是一种高效的数据结构,它通过特定的算法——哈希函数,将任意大小的键(key)映射到一个固定大小的数组索引上,从而...理解其工作原理,并熟练掌握其C语言实现,对于提升编程能力及解决实际问题具有重要意义。

    C语言实现哈希表(源码+解析)

    哈希表结构 struct HashTable:表示哈希表,包含一个存储节点指针的数组。 创建哈希表函数 createHashTable:动态分配哈希表的内存,并初始化哈希表数组为NULL。 哈希函数 hashCode:根据键计算哈希值,采用简单的...

    C语言实现简单易用哈希表

    在这个C语言实现中,由于文件名`strmap.h`和`strmap.c`暗示了每个键值对可能是独立的结构体,因此可能采用了链地址法,即每个数组元素实际上是一个链表的头,链表中存储冲突的键值对。 4. **键值对结构**:每个键值...

    哈希表查找(C语言实现)

    三、C语言实现哈希表查找 在C语言中,可以使用结构体来表示哈希表和哈希表中的节点。例如: ```c typedef struct HashNode { int key; int value; struct HashNode* next; // 链地址法时的指针 } HashNode; ...

    基于C语言实现电子英汉词典

    总之,通过C语言实现电子英汉词典是一个涉及数据结构、算法和文件操作的综合实践。这个项目不仅锻炼了编程技能,也提供了对C语言特性和程序设计原则的深入理解。通过学习和实现这个项目,开发者可以更好地掌握C语言...

    HashTable

    《深入解析HashTable:C语言实现的精髓》 在计算机科学中,哈希表(HashTable)是一种数据结构,它实现了关联数组的抽象数据类型,能够快速地进行查找、插入和删除操作。哈希表通过将键(Key)映射到表中的一个位置...

    哈希表的设计与实现C语言

    在C语言中,实现哈希表需要理解其基本原理,并掌握如何利用C语言的数据结构和内存管理来构建哈希表。 哈希函数是哈希表的核心,它的目标是将键转化为数组索引。一个好的哈希函数应该能尽可能均匀地分布键值,以减少...

Global site tag (gtag.js) - Google Analytics