Linux glib库hash表GHashTable介绍
GHashTable
- 1 简单使用
- 2 原理分析
- 3 思考总结
1 简单使用
hash表是一种提供key-value访问的数据结构,通过指定的key值可以快速的访问到与它相关联的value值。hash表的一种典型用法就是字典,通过单词的首字母能够快速的找到单词。
要使用一个hash表首先必须创建它,glib库里有两个函数可以用于创建hash表,分别是g_hash_table_new()和g_hash_table_new_full(),它们的原型如下:
GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func);GHashTable* g_hash_table_new_full(GHashFunc hash_func,GEqualFunc key_equal_func,GDestroyNotify key_destroy_func,GDestroyNotify value_destroy_func);
其中hash_func
是一个函数,它为key创建一个hash值;key_equal_func用于比较两个key是否相等;
key_destroy_func
当你从hash表里删除、销毁一个条目时,glib库会自动调用它释放key所占用的内存空间,这对于key是动态分配内存的hash表来说非常有用;
value_destroy_func
的作用与key_destroy_func相似,
只是它释放的是value占用的内存空间。
下面以一段代码来演示glib库中hash表的基本用法:
/***************************************************************************file: g_hash.cdesc: 这个文件用于演示glib库中hash表的用法compile: gcc -o g_hash g_hash.c `pkg-config --cflags --libs glib-2.0`***************************************************************************/#include <glib.h>void print_key_value(gpointer key, gpointer value, gpointer user_data);void display_hash_table(GHashTable *table);void free_key(gpointer data);void free_value(gpointer value);void print_key_value(gpointer key, gpointer value, gpointer user_data){printf("%s ---> %s/n", key, value);}void display_hash_table(GHashTable *table){g_hash_table_foreach(table, print_key_value, NULL);}void free_key(gpointer data){printf("We free key: %s /n", data);free(data);}void free_value(gpointer data){printf("We free value: %s /n", data);free(data);}int main(int argc, char *argv[]){GHashTable *table = NULL;table = g_hash_table_new(g_str_hash, g_str_equal);g_hash_table_insert(table, "k", "one");g_hash_table_insert(table, "k", "two");g_hash_table_insert(table, "k", "three");g_hash_table_insert(table, "k", "four");g_hash_table_insert(table, "k", "five");display_hash_table(table);printf("Size of hash table: %d /n", g_hash_table_size(table));printf("Before replace: 3 ---> %s /n", g_hash_table_lookup(table, "k"));g_hash_table_replace(table, "k", "third");printf("After replace: 3 ---> %s /n", g_hash_table_lookup(table, "k"));g_hash_table_remove(table, "k");display_hash_table(table);printf("Now size of hash table: %d /n", g_hash_table_size(table));g_hash_table_destroy(table);table = g_hash_table_new_full(g_str_hash, g_str_equal, free_key, free_value);g_hash_table_insert(table, strdup("one"), strdup("first"));g_hash_table_insert(table, strdup("two"), strdup("second"));g_hash_table_insert(table, strdup("three"), strdup("third"));printf("Remove an item from hash table: /n");g_hash_table_remove(table, "two");printf("Destroy hash table: /n");g_hash_table_destroy(table);return ;}
输出如图:
2 原理分析
GLIBC中HASH表处理冲突的方式是哈希算法的“开放定址”法。
我们先来看一下GHashTable的结构:
struct _GHashTable
{gint size;gint mod;guint mask;gint nnodes;gint noccupied; /* nnodes + tombstones */gpointer *keys; //用于存放每一个keyguint *hashes; //用于存放每一个key所对映的hash值gpointer *values; //用于存放每一个key所对映的value//如果有一个key = 'age',经过计算得到这个key的hash值为431,并且这个key对映的值为22,则这三个数据分别存放在上面三个数组里GHashFunc hash_func;GEqualFunc key_equal_func;gint ref_count;
#ifndef G_DISABLE_ASSERT/** Tracks the structure of the hash table, not its contents: is only* incremented when a node is added or removed (is not incremented* when the key or data of a node is modified).*/int version;
#endifGDestroyNotify key_destroy_func;GDestroyNotify value_destroy_func;
};
看一下hashtable这个数据结构中值得关注的成员:
size:hashtable的大小,即三个数组的大小,这个大小会随着数据的添加而不断变化。初始值为8
mod:hashtable的被除数,glib使用除法散列法来避免冲突。
mask:size - 1
nnodes:总有多少对(key,value)
在调用g_hash_table_new[_full]函数时,hashtable的各个成员被初始化,这个可以查看源代码:
GHashTable*
g_hash_table_new_full (GHashFunc hash_func,GEqualFunc key_equal_func,GDestroyNotify key_destroy_func,GDestroyNotify value_destroy_func)
{GHashTable *hash_table;hash_table = g_slice_new (GHashTable);g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT); //将size设为8hash_table->nnodes = 0;hash_table->noccupied = 0;hash_table->hash_func = hash_func ? hash_func : g_direct_hash;hash_table->key_equal_func = key_equal_func;hash_table->ref_count = 1;
#ifndef G_DISABLE_ASSERThash_table->version = 0;
#endifhash_table->key_destroy_func = key_destroy_func;hash_table->value_destroy_func = value_destroy_func;hash_table->keys = g_new0 (gpointer, hash_table->size);hash_table->values = hash_table->keys; hash_table->hashes = g_new0 (guint, hash_table->size);//初始化三个函数return hash_table;
}
在调用g_hash_table_insert时,我们可以看出来glib是如何实现hashtable的。我们先来看看glib是如何实现插入的。
static void
g_hash_table_insert_internal (GHashTable *hash_table,gpointer key,gpointer value,gboolean keep_new_key)
{guint key_hash;guint node_index;g_return_if_fail (hash_table != NULL);node_index = g_hash_table_lookup_node (hash_table, key, &key_hash);g_hash_table_insert_node (hash_table, node_index, key_hash, key, value, keep_new_key, FALSE);
}
上面的代码实现了插入操作,g_hash_table_lookup_node函数接受一个key变量,他会返回这个key相对映的下标,利用这个下标node_index,可以方便的得到这个key对映的value和hash——values[node_index]和hashs[node_index]。如果这是个新的key,那函数就会返回一个现在并没有被使用的下标。然后g_hash_table_insert_node就会将这个hash对(key, value)插入hash表中。
glib实现插入时会对原有的key进行检查,判断这个key是否已经存在了,如果已经存在就更改这个key对映的value,如果没有的话就添加key,hash,value。现在我们可以来看看glib是如何实现g_hash_table_lookup_node函数的。
hash表尽管使用了各种方法做hash函数,但是冲撞总是不可避免的。当我们使用除法散列时,每一个hash值对映的下标为hash%mod,如果一个key的hash值为10,另一个key的hash值为17,而被除数为7的话,这两个key就会发生冲撞。假设10已经插到下标为3(10%7)的数组里了,这时要插入17,我们只能向下查找,如果4的位置是空的,我们就可以插入到4当中了。如此循环,我们一定能找一个空的位置。但是,如果4这个位置已经被11占用后又释放了,我们需要一个标志,即使是释放了,也不能直接设为空,这个标志就是tombstone。
g_hash_table_lookup_node (GHashTable *hash_table,gconstpointer key,guint *hash_return)
{guint node_index;guint node_hash;guint hash_value;guint first_tombstone = 0;gboolean have_tombstone = FALSE;guint step = 0;hash_value = hash_table->hash_func (key);if (G_UNLIKELY (!HASH_IS_REAL (hash_value)))hash_value = 2;*hash_return = hash_value;node_index = hash_value % hash_table->mod; //得到这个hash值对映的下标,比如10%7 =3,则这个下标为3node_hash = hash_table->hashes[node_index]; //取出下标为3的hashs数组中的值,判断他是否被占用了while (!HASH_IS_UNUSED (node_hash)) //当node_hash不等于0时,进入循环{/* We first check if our full hash values* are equal so we can avoid calling the full-blown* key equality function in most cases.*/if (node_hash == hash_value) //当hash值相等时,判断key是否相等,如果相等则返回下标值。{gpointer node_key = hash_table->keys[node_index];if (hash_table->key_equal_func){if (hash_table->key_equal_func (node_key, key))return node_index;}else if (node_key == key){return node_index;}}else if (HASH_IS_TOMBSTONE (node_hash) && !have_tombstone) //当hash值不相等是,判断是否为tombstone{first_tombstone = node_index;have_tombstone = TRUE;}step++;node_index += step;node_index &= hash_table->mask;node_hash = hash_table->hashes[node_index];}if (have_tombstone)return first_tombstone;return node_index;
}
当找到给定的key时,函数返回下标值,如果没有的话,就返回tombstone的位置或者第一个空的位置。
现在我们得到了一个下标值了,我们可以将值插入到hash表中了
static void
g_hash_table_insert_node (GHashTable *hash_table,guint node_index,guint key_hash,gpointer key,gpointer value,gboolean keep_new_key,gboolean reusing_key)
{guint old_hash;gpointer old_key;gpointer old_value;if (G_UNLIKELY (hash_table->keys == hash_table->values && key != value))hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size);old_hash = hash_table->hashes[node_index];old_key = hash_table->keys[node_index];old_value = hash_table->values[node_index];if (HASH_IS_REAL (old_hash)) //原hash表中已经被占用了,说明给定的key已经存在在hash表中了{if (keep_new_key)hash_table->keys[node_index] = key;hash_table->values[node_index] = value;}else{hash_table->keys[node_index] = key;hash_table->values[node_index] = value;hash_table->hashes[node_index] = key_hash;hash_table->nnodes++;if (HASH_IS_UNUSED (old_hash)){/* We replaced an empty node, and not a tombstone */hash_table->noccupied++;g_hash_table_maybe_resize (hash_table);}#ifndef G_DISABLE_ASSERThash_table->version++;
#endif}if (HASH_IS_REAL (old_hash)){if (hash_table->key_destroy_func && !reusing_key)hash_table->key_destroy_func (keep_new_key ? old_key : key);if (hash_table->value_destroy_func)hash_table->value_destroy_func (old_value);}
}
上面的代码比较容易理解,就是先判断key是否已经存在了,如果存在就替换到他所对映的value。如果不存在就添加这对(key,value)。再判断是否需要调整hash表的大小。
3 思考总结
如果诸位兄台想了解glibc中HASH详细介绍的还是看Bean_lee兄的相关介绍,http://blog.chinaunix.net/uid-24774106-id-3605760.html分析得相当到位!
对API感兴趣的可以去此处https://developer.gnome.org/glib/unstable/glib-Hash-Tables.html#g-hash-table-unref,
对源码看兴趣的可以去此处http://ftp.gnome.org/pub/GNOME/sources/glib/
Linux glib库hash表GHashTable介绍相关推荐
- linux 2.6 hash表作用,高性能分布式哈希表FastDHT介绍及安装配置
FastDHT介绍 FastDHT 是一个高性能的分布式哈希系统 (DHT) ,使用 Berkeley DB 做数据存储,使用 libevent 做网络IO处理,提供 Java 版的客户端接口包.适合 ...
- mysql 向下兼容_前言本文主要介绍的是关于Mysql8.0驱动getTables返回所有库的表的相关内容,MySQL Connector/J 8.0版本驱动向下兼容之前的5.5...
前言 本文主要介绍的是关于Mysql8.0驱动getTables返回所有库的表的相关内容,MySQL Connector/J 8.0版本驱动向下兼容之前的5.5+版本MySQL,如果你使用的是5.5+ ...
- linux服务器都有注册表吗,NT服务器的注册表结构介绍
NT服务器的注册表结构介绍 HKEY_CLASSES_ROOT:它包括与OLE和文件关联有关的信息.设置这一项的目的是提供和目前的Windows 3.x相兼容. HKEY_CURRENT_USER:它 ...
- linux中mysql如何删除库,Linux环境下MySQL基础命令(2)----查看、创建、删除库和表...
案例二,查看数据库 MySQL是一套数据库管理系统,支持运行多个库,每个库相当于一个容器,存放着许多表.数据就存在这些表中,所以查看.创建.删除库和表,在工作中必不可少. 一,查看 1,查看当前服务器 ...
- mysql的删除命令+linux命令大全,Linux环境下MySQL基础命令----查看、创建、删除库和表...
案例二,查看数据库 MySQL是一套数据库管理系统,支持运行多个库,每个库相当于一个容器,存放着许多表.数据就存在这些表中,所以查看.创建.删除库和表,在工作中必不可少. 一,查看 1,查看当前服务器 ...
- Linux hash表
哈希表的意义就是快速定位查找,使用key值快速定位出数据位置. 流程图 查找时根据key值算出nodes的下标地址:(h->can_ch + h->can_id) % bucktes;,这 ...
- hash表的平均查找长度C语言,关于ASL(平均查找长度)的简单总结
ASL(Average Search Length),即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数成为平均查找长度. 它的定义是这样的: 其中n ...
- GTK+编程入门(1)—简介与glib库
GTK+编程入门(1)-简介与glib库(2015-7-23) 分类:GTK+ 一:GTK+简介 GTK+是一个软件开发工具包,其设计目的是支持在 X Window系统下开发图形界面的应用程序.G ...
- 从头到尾彻底解析Hash表算法
从头到尾彻底解析Hash表算法 发布时间: 2013-10-02 10:26 阅读: 25156 次 推荐: 14 原文链接 [收藏] 作者:July.wuliming.pkuoliv ...
最新文章
- composer 小技巧
- 【数据竞赛】NLP竞赛中99%上升的技巧!
- 【软考-软件设计师】计算机存储系统
- [HTML5amp;CSS3]Transform具体解释
- 1.C#基础之简介(完成)
- I am late!
- ftp linux 服务器 麒麟_麒麟系统安装ftp
- 关于子元素连续数字和英文内容溢出父元素的问题
- 优先队列(priority queue)的实现(java,jdk接口)
- python中pip有啥用_python的pip有什么用
- Mellanox IB卡介绍
- Google可能退出中国
- quartz 定时任务不运行问题
- 给大家推荐个jquery api 3.2 在线手册地址
- 利用 Maxima 求解常微分方程
- 非线性方程的数值解法:牛顿法及牛顿下山法(含Matlab程序)
- 大邑移动团支部学校联谊活动
- 微信小程序 图片等比例缩-放(图片自适应屏幕)
- Benchmark(基准测试)初相识
- vue的html自动刷新,vue项目刷新当前页面的方法