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介绍相关推荐

  1. linux 2.6 hash表作用,高性能分布式哈希表FastDHT介绍及安装配置

    FastDHT介绍 FastDHT 是一个高性能的分布式哈希系统 (DHT) ,使用 Berkeley DB 做数据存储,使用 libevent 做网络IO处理,提供 Java 版的客户端接口包.适合 ...

  2. mysql 向下兼容_前言本文主要介绍的是关于Mysql8.0驱动getTables返回所有库的表的相关内容,MySQL Connector/J 8.0版本驱动向下兼容之前的5.5...

    前言 本文主要介绍的是关于Mysql8.0驱动getTables返回所有库的表的相关内容,MySQL Connector/J 8.0版本驱动向下兼容之前的5.5+版本MySQL,如果你使用的是5.5+ ...

  3. linux服务器都有注册表吗,NT服务器的注册表结构介绍

    NT服务器的注册表结构介绍 HKEY_CLASSES_ROOT:它包括与OLE和文件关联有关的信息.设置这一项的目的是提供和目前的Windows 3.x相兼容. HKEY_CURRENT_USER:它 ...

  4. linux中mysql如何删除库,Linux环境下MySQL基础命令(2)----查看、创建、删除库和表...

    案例二,查看数据库 MySQL是一套数据库管理系统,支持运行多个库,每个库相当于一个容器,存放着许多表.数据就存在这些表中,所以查看.创建.删除库和表,在工作中必不可少. 一,查看 1,查看当前服务器 ...

  5. mysql的删除命令+linux命令大全,Linux环境下MySQL基础命令----查看、创建、删除库和表...

    案例二,查看数据库 MySQL是一套数据库管理系统,支持运行多个库,每个库相当于一个容器,存放着许多表.数据就存在这些表中,所以查看.创建.删除库和表,在工作中必不可少. 一,查看 1,查看当前服务器 ...

  6. Linux hash表

    哈希表的意义就是快速定位查找,使用key值快速定位出数据位置. 流程图 查找时根据key值算出nodes的下标地址:(h->can_ch + h->can_id) % bucktes;,这 ...

  7. hash表的平均查找长度C语言,关于ASL(平均查找长度)的简单总结

    ASL(Average Search Length),即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数成为平均查找长度. 它的定义是这样的: 其中n ...

  8. GTK+编程入门(1)—简介与glib库

    GTK+编程入门(1)-简介与glib库(2015-7-23) 分类:GTK+ 一:GTK+简介   GTK+是一个软件开发工具包,其设计目的是支持在 X Window系统下开发图形界面的应用程序.G ...

  9. 从头到尾彻底解析Hash表算法

    从头到尾彻底解析Hash表算法 发布时间: 2013-10-02 10:26  阅读: 25156 次  推荐: 14   原文链接   [收藏]   作者:July.wuliming.pkuoliv ...

最新文章

  1. composer 小技巧
  2. 【数据竞赛】NLP竞赛中99%上升的技巧!
  3. 【软考-软件设计师】计算机存储系统
  4. [HTML5amp;CSS3]Transform具体解释
  5. 1.C#基础之简介(完成)
  6. I am late!
  7. ftp linux 服务器 麒麟_麒麟系统安装ftp
  8. 关于子元素连续数字和英文内容溢出父元素的问题
  9. 优先队列(priority queue)的实现(java,jdk接口)
  10. python中pip有啥用_python的pip有什么用
  11. Mellanox IB卡介绍
  12. Google可能退出中国
  13. quartz 定时任务不运行问题
  14. 给大家推荐个jquery api 3.2 在线手册地址
  15. 利用 Maxima 求解常微分方程
  16. 非线性方程的数值解法:牛顿法及牛顿下山法(含Matlab程序)
  17. 大邑移动团支部学校联谊活动
  18. 微信小程序 图片等比例缩-放(图片自适应屏幕)
  19. Benchmark(基准测试)初相识
  20. vue的html自动刷新,vue项目刷新当前页面的方法

热门文章

  1. Flask Web开发:基于Python的Web应用开发实战
  2. spring boot 超市进销存系统源码
  3. 山师计算机组成原理期末,山师—2010级计算机组成原理期末试卷(B)历山
  4. GD32F103C8T6点灯
  5. 大一上计算机导论期末试题及答案,郑州大学计算机导论大一试题答案.doc
  6. 线性代数:秩的各章节串烧、秩的所有公式总结、秩的常用结论及其推论、线性相关与线性表示线性表出和秩的关系
  7. SER 语音情感识别-论文笔记1
  8. 快速扩展在线文档产品特性的9个开源项目
  9. 嵌入式Linux驱动:LRADC按键
  10. Git GitHub 入门篇