花了一天时间用c写了一个类似java中的HashMap,接受大家的检阅

test_lockxxx 2012-05-04 06:15:15
在java中像ArrayList,HashMap都是现成的,在java.util包中,用的时候直接import java.util.*就行了。

前几天写了一c版的ArrayList,同时欢迎大家指出问题:
http://topic.csdn.net/u/20120429/18/4ab4bc02-2496-4d3c-8151-1cbe51e6fe9d.html?seed=425415324&r=78416084

今天有空,也为了练习一下c,我就参照着jdk中的HashMap源码,写了一个c版的HashMap,这个HashMap多少有点泛型的特征<E>。

关于这个HashMap,现在有一个困惑,在hashmap_remove()中,要不要在返回前free()?
直接上代码,接受大家的检阅(欢迎大家指出问题):

hashmap.h

#ifndef HASHMAP_H_INCLUDED
#define HASHMAP_H_INCLUDED

//HashMap中的链表
typedef struct _entry
{
int hash;
void *key;
void *value;
struct _entry *next; //单向链表

} *Entry;

//主要用于hashmap_keySet()
typedef struct _hashmapset
{
void *key;
struct _hashmapset *next;

} *HashMapSet;

//hashmap 结构
typedef struct _hashmap
{
int K_E; //类似java中的泛型
int V_E; //类似java中的泛型
int size;
int capacity; //当前容量
int threshold; //门槛
float factor; //扩展因子,当空间不够用时
Entry table;

} *HashMap;

#ifndef E_H_INCLUDED
#define E_H_INCLUDED
enum E{E_STRING,E_INT,E_OBJECT}; //枚举类型
#endif // E_H_INCLUDED

void *hashmap_init(int k,int v); //初始化

void *hashmap_put(HashMap this,void *k,void *v); //在此映射中关联指定值与指定键。

void *hashmap_get(HashMap this,void *k); //返回指定键在此标识哈希映射中所映射的值,如果对于此键来说,映射不包含任何映射关系,则返回 null

void *hashmap_remove(HashMap this,void *k); //如果此映射中存在该键的映射关系,则将其删除。

HashMapSet hashmap_keySet(HashMap this); //返回此映射中所包含的键的 set 视图。

void hashmap_clear(HashMap this); //从此映射中移除所有映射关系。

void hashmap_free(HashMap this); //释放内存


#endif // HASHMAP_H_INCLUDED


...全文
798 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
sniperhuangwei 2012-06-29
  • 打赏
  • 举报
回复
http://www.cnblogs.com/sniperHW/archive/2012/04/02/2429607.html
有一个系列
test_lockxxx 2012-05-05
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
刚学完c(谭浩强版那本书),正学java中的白菜,看到Lz如此,我觉得,我连c的皮毛都没学到。帮忙顶了,感谢LZ分享,但是,我以后也会写出这般代码的!鼓励自己一下。哈,谢谢lz分享。
[/Quote]

99年的时候,我初次自学c语言(当时买的书好像就是谭浩强写的),学了一段时间后,在一次上机课上,试着写一个99乘法表,用了两节课的时间也没有写出来 !-_-,更不知指针为何物,后来实在学不下去才放弃。

后来自学了 Qbasic -> vb6 -> java 至今。

去年不太忙,就重新学c语言,网购了一本《C程序设计语言》(第2版-新版),没事的时候翻开看几页,差不多一个月时间看完了,平时工作时基本上用不到c,所以写这些代码,算是练习一下了,全当时温故而知新吧。

学习和使用java的时候,算法方面很少接触,因为java已提供了很多实用的类(比如java.util包)都实现了我们日常所需要的功能。在很多地方看到很多人提到“结构与算法”对编程的重要性,学习java的时候,真的没体会到这一点,不过现在重新学习c语言,才体会到"结构与算法"对c的意义。
test_lockxxx 2012-05-05
  • 打赏
  • 举报
回复
今天早上测试 hashmap的key = 0(NULL)的时候,发现hashmap_keySet()函数有一个问题:

修改后的hashmap_keySet()如下:


//返回此映射中所包含的键的 set 视图
//注意在调用该函数的代码段中,处理完HashMapSet后,要free(HashMapSet);
HashMapSet hashmap_keySet(HashMap this)
{
//head 结点
HashMapSet set = malloc(sizeof(struct _hashmapset));
set->key = NULL;
set->next = NULL;

int i;
Entry e;
HashMapSet preSet = set; //上一个Set

int capacity = this->capacity;
for(i=0; i<capacity; i++)
{
for(e = this->table + i,e=e->next; e!=NULL; e=e->next)
{
//添加一个新结点
HashMapSet item = malloc(sizeof(struct _hashmapset));
item->key = e->key;
item->next = NULL;

preSet->next = item;

preSet = item; //
}
}

return set->next;
}
lbq199204 2012-05-04
  • 打赏
  • 举报
回复
刚学完c(谭浩强版那本书),正学java中的白菜,看到Lz如此,我觉得,我连c的皮毛都没学到。帮忙顶了,感谢LZ分享,但是,我以后也会写出这般代码的!鼓励自己一下。哈,谢谢lz分享。
test_lockxxx 2012-05-04
  • 打赏
  • 举报
回复
测试hashmap用的例子:

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"

typedef struct object
{
char *name;
int age;
} *Object;

int main()
{
//初始化
HashMap map = hashmap_init(E_STRING,E_INT);

//检查hash碰撞,比如 'rQ' 和 'qp' 的 hashcode 一样
hashmap_put(map,"rQ",1);
hashmap_put(map,"qp",2);

hashmap_put(map,"a1",1);
hashmap_put(map,"a2",2);
hashmap_put(map,"a3",3);

hashmap_put(map,"b1",1);
hashmap_put(map,"b2",2);
hashmap_put(map,"b3",3);
hashmap_put(map,"b4",4);
hashmap_put(map,"b5",5);
hashmap_put(map,"b6",6);
hashmap_put(map,"b7",7);
hashmap_put(map,"b8",8);
hashmap_put(map,"b9",9);

char *p = malloc(sizeof(char) * 10);
strcpy(p,"a1");
hashmap_put(map,p,1);

hashmap_put(map,"rQrQ",5);
hashmap_put(map,"rQqp",6);
hashmap_put(map,"qprQ",7);
hashmap_put(map,"qpqp",8);

hashmap_put(map,"你1好",11);
hashmap_put(map,"你2好",12);
hashmap_put(map,"你3好",13);


printf("----------------------------put\n");
printf("put(rQqp):%d\n",hashmap_put(map,"rQqp",60));
printf("put(qprQ):%d\n",hashmap_put(map,"qprQ",60));


printf("----------------------------get\n");
char *t = malloc(sizeof(char) * 10);
strcpy(t,"a1");
printf("get(a1):%d\n",hashmap_get(map,t));

printf("get(rQqp):%d\n",hashmap_get(map,"rQqp"));
printf("get(qpqp):%d\n",hashmap_get(map,"qpqp"));
printf("get(你1好):%d\n",hashmap_get(map,"你1好"));
printf("get(你2好):%d\n",hashmap_get(map,"你2好"));
printf("get(你3好):%d\n",hashmap_get(map,"你3好"));


printf("----------------------------remove\n");
printf("remove(你1好):%d\n",hashmap_remove(map,"你1好"));
printf("remove(qpqp):%d\n",hashmap_remove(map,"qpqp"));
printf("\n");
printf("get(rQqp):%d\n",hashmap_get(map,"rQqp"));
printf("get(qpqp):%d\n",hashmap_get(map,"qpqp"));
printf("get(你1好):%d\n",hashmap_get(map,"你1好"));
printf("get(你2好):%d\n",hashmap_get(map,"你2好"));
printf("get(你3好):%d\n",hashmap_get(map,"你3好"));

printf("----------------------------keySet\n");
printf("size:%d\n",map->size);

HashMapSet set;
for(set = hashmap_keySet(map); set!=NULL; set=set->next)
{
printf("%s:%d\n",set->key,hashmap_get(map,set->key));
}

printf("----------------------------clear\n");
hashmap_clear(map);
printf("size:%d\n",map->size);

//HashMapSet set;
for(set = hashmap_keySet(map); set!=NULL; set=set->next)
{
printf("%s:%d\n",set->key,hashmap_get(map,set->key));
}


return 0;
}

test_lockxxx 2012-05-04
  • 打赏
  • 举报
回复
hashmap.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"

//参考了java的HashMap.java中的hash算法
static int hash(int h)
{
/*
//java
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
*/

unsigned int x = h; //由于要无符号右移,左边补0
x ^= (x >> 20) ^ (x >> 12);
return x ^ (x >> 7) ^ (x >> 4);

}

//参考了java的HashMap.java中的indexFor()
static int indexFor(int h, int length)
{
return h & (length-1);
}

//字符串的hashcode,改写java的String.hashCode();
static int stringHashCode(char *pStr)
{
int size = strlen(pStr);

if(size == 0)
{
return 0;
}
else
{
int i,h=0;
for(i=0; i<size; i++)
{
h = 31 * h + *pStr;
pStr++;
}

return h;
}
}


//扩大容量
static void resize(HashMap this,int newCapacity)
{
//printf("resize 前, capacity:%d,threshold:%d\n",this->capacity,this->threshold);

int oldCapacity = this->capacity;

Entry newTable = malloc(sizeof(struct _entry) * newCapacity);

//初始化为NULL;
int i;
for(i=0; i<newCapacity; i++)
{
(newTable + i)->next = NULL;
}

//由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内。
//由于hash表的容量扩大,indexFor(int hash,int length)由于length改变,返回值也将改变,所以此处不能简单的使用memcpy
//memcpy(newTable,this->table,sizeof(struct _entry) * oldCapacity);

//重新确定新的位置indexFor(....)
//以下实现参照java的HashMap.java中的void transfer(Entry[] newTable)部分
int m,n;
for(m=0; m<oldCapacity; m++)
{
Entry e = (this->table + m)->next;

if(e!=NULL)
{
do
{
Entry next = e->next;

n = indexFor(e->hash,newCapacity);

e->next = (newTable + n)->next;

(newTable + n)->next = e;

e = next;
}
while(e!=NULL);
}
}

this->table = newTable;
this->capacity = newCapacity;
this->threshold = (int)(newCapacity * this->factor);

//printf("resize 后, capacity:%d,threshold:%d\n\n",this->capacity,this->threshold);
}

//添加一个结点到链表中
static void addEntry(HashMap this,void *k,void *v,int hashCode,int i)
{
Entry e = this->table + i; //先定位到一个链表的head 结点

//添加到链表中
do
{
if(e->next == NULL)
{
//新的结点
Entry item = malloc(sizeof(struct _entry));
item->hash = hashCode;
item->key = k;
item->value = v;
item->next = NULL;

e->next = item; //加入链表中

break;
}
}
while((e = e->next) != NULL);

this->size++; //size + 1

if (this->size >= this->threshold)
{
resize(this,2 * this->size); //扩大一倍容量
}
}



//初始化
void *hashmap_init(int k_e,int v_e)
{
HashMap map = malloc(sizeof(struct _hashmap));
map->K_E = k_e;
map->V_E = v_e;
map->size = 0;
map->capacity = 16;
map->factor = 0.75f;
map->threshold = (int)(map->capacity * map->factor);

map->table = malloc(sizeof(struct _entry) * map->capacity);

//初始化为NULL
int i;
int capacity = map->capacity;
for(i=0; i<capacity; i++)
{
(map->table+i)->next = NULL; //初始化

//printf("init %d address:%d\n",i,map->table + i);
}

//printf("capacity address:%d\n",&map->capacity);

return map;
}

//在此映射中关联指定值与指定键。
void *hashmap_put(HashMap this,void *k,void *v)
{
int hashCode;
if(this->K_E == E_STRING)
{
hashCode = hash(stringHashCode(k));

//printf("-----------%s:%d\n",k,hashCode);
}
else if(this->K_E == E_INT)
{
hashCode = hash(k);
}
else
{
hashCode = hash(k);
}

int i = indexFor(hashCode,this->capacity);
Entry e;

if(this->K_E == E_STRING || this->K_E == E_INT)
{
for(e = this->table + i,e=e->next; e!=NULL; e=e->next)
{
if((e->hash == hashCode) && (e->key == k || strcmp(e->key,k) == 0))
{
void *oldValue = e->value; //取原来的旧的值
e->value = v; //设置新值

return oldValue; //返回旧值
}
}
}
else
{
//这里不太完善,有待改进
for(e = this->table + i,e=e->next; e!=NULL; e=e->next)
{
if((e->hash == hashCode) && e->key == k)
{
void *oldValue = e->value; //取原来的旧的值
e->value = v; //设置新值

return oldValue; //返回旧值
}
}
}

//新的
addEntry(this,k,v,hashCode,i);
return NULL;
}


//返回指定键在此标识哈希映射中所映射的值,如果对于此键来说,映射不包含任何映射关系,则返回 null。
void *hashmap_get(HashMap this,void *k)
{
int hashCode;

if(this->K_E == E_STRING)
{
hashCode = hash(stringHashCode(k));
}
else if(this->K_E == E_INT)
{
hashCode = hash(k);
}
else
{
hashCode = hash(k);
}

int i = indexFor(hashCode,this->capacity);
Entry e;

if(this->K_E == E_STRING || this->K_E == E_INT)
{
for(e=this->table + i,e=e->next; e!=NULL; e=e->next)
{
if(e->hash == hashCode && (e->key == k || strcmp(e->key,k) == 0))
{
return e->value;
}
}

return NULL;
}
else
{
//这里不太完善,有待改进
for(e=this->table + i,e=e->next; e!=NULL; e=e->next)
{
if(e->hash == hashCode && e->key == k)
{
return e->value;
}
}

return NULL;
}

return NULL;

}


//如果此映射中存在该键的映射关系,则将其删除。
//注意在返回处理完成后,free();
void *hashmap_remove(HashMap this,void *k)
{
int hashCode;

if(this->K_E == E_STRING)
{
hashCode = hash(stringHashCode(k));
}
else if(this->K_E == E_INT)
{
hashCode = hash(k);
}
else
{
hashCode = hash(k);
}

int i = indexFor(hashCode,this->capacity);
Entry e;
Entry preE=NULL; //上一个

for(e=this->table+i,preE=e,e=e->next; e!=NULL; preE=e,e=e->next)
{
if(e->hash == hashCode && (e->key == k || strcmp(e->key,k) == 0))
{
if(e->next == NULL)
{
preE->next = NULL;
}
else
{
preE->next = e->next; //将此结点删除
}

e->next = NULL; //设置为NULL

this->size--; //size - 1

//free(e); //这里释放内存后,为什么下面还能正常返回值?

return e->value;
}
}

return NULL;
}

//返回此映射中所包含的键的 set 视图
//注意在返回处理完成后,free();
HashMapSet hashmap_keySet(HashMap this)
{
int size = this->size;

if(size == 0)
{
return NULL;
}
else
{
HashMapSet set = malloc(sizeof(struct _hashmapset));
set->key = NULL;
set->next = NULL;

int i;
Entry e;
HashMapSet preSet = set; //上一个Set

int capacity = this->capacity;
for(i=0; i<capacity; i++)
{
for(e = this->table + i,e=e->next; e!=NULL; e=e->next)
{
if(preSet->key == NULL)
{
//第一次赋值
preSet->key = e->key;
preSet->next = NULL;
}
else
{
//添加一个新结点
HashMapSet item = malloc(sizeof(struct _hashmapset));
item->key = e->key;
item->next = NULL;

preSet->next = item;

preSet = item; //
}
}
}

return set;
}
}


//从此映射中移除所有映射关系。
void hashmap_clear(HashMap this)
{
int capacity = this->capacity;
int i;
Entry e,item;
for(i=0; i<capacity; i++)
{
for(e = this->table + i,e=e->next; e!=NULL; item = e,e=e->next,free(item))
{
}

(this->table + i)->next = NULL; //重置为NULL
}

//free(this->table);

this->size = 0;
}


//释放内存
void hashmap_free(HashMap this)
{
int capacity = this->capacity;
Entry e,item;
int i;
for(i=0; i<capacity; i++)
{
for(e=this->table + i, e=e->next; e!=NULL; item=e,e=e->next,free(item))
{
}
}

this->capacity = 0;
this->size = 0;

free(this->table);
free(this);
}

qq120848369 2012-05-04
  • 打赏
  • 举报
回复
感谢分享.

69,747

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧