一种用于 C/C++ 的简单单标头哈希图实现。
只需在代码中#include "hashmap.h"
即可!
当前支持的编译器有 gcc、clang 和 msvc。
当前支持的平台有 Linux、macOS 和 Windows。
哈希映射可与任何任意数据键一起使用 - 您只需提供指针和大小,它就会对该数据进行哈希处理。如果可能的话,默认哈希器是使用硬件内在函数的 crc32 变体,默认比较器仅使用memcmp
,因此建议将结构键中的任何填充清零。
要创建 hashmap,请调用hashmap_create
函数:
const unsigned initial_size = 2 ;
struct hashmap_s hashmap ;
if ( 0 != hashmap_create ( initial_size , & hashmap )) {
// error!
}
initial_size
参数仅设置哈希映射的初始大小 - 如果多个键命中相同的哈希条目,则该大小可能会增长。哈希图的大小向上舍入到所提供的initial_size
之上最接近的2的幂。
还有一个扩展创建函数hashmap_create_ex
:
struct hashmap_s hashmap ;
struct hashmap_create_options_s options ;
memset ( & options , 0 , sizeof ( options ));
// You can set a custom hasher that the hashmap should use.
options . hasher = & my_hasher ;
// You can set a custom comparer that the hashmap should for comparing keys.
options . comparer = & my_comparer ;
// You can also specify the initial capacity of the hashmap.
options . initial_capacity = 42 ;
if ( 0 != hashmap_create_ex ( options , & hashmap )) {
// error!
}
要将项目放入哈希图中,请使用hashmap_put
函数:
int meaning_of_life = 42 ;
char question = 6 * 8 ;
if ( 0 != hashmap_put ( & hashmap , "life" , strlen ( "life" ), & meaning_of_life )) {
// error!
}
if ( 0 != hashmap_put ( & hashmap , "?" , strlen ( "?" ), & question )) {
// error!
}
请注意,同一哈希图中可以存在不同类型的多个条目。哈希映射不是类型化的 - 它可以存储任何void*
数据作为键的值。
要从哈希映射中获取条目,请使用hashmap_get
函数:
void * const element = hashmap_get ( & hashmap , "x" , strlen ( "x" ));
if ( NULL == element ) {
// error!
}
如果未找到该元素,该函数将返回NULL
。请注意,用于获取元素的键不必与用于将元素放入哈希图中的指针相同 - 但字符串切片必须匹配才能发生命中。
要从哈希图中删除条目,请使用hashmap_remove
函数:
if ( 0 != hashmap_remove ( & hashmap , "x" , strlen ( "x" ))) {
// error!
}
如果未找到该元素,该函数将返回非零值。请注意,用于获取元素的键不必与用于将元素放入哈希图中的指针相同 - 但字符串切片必须匹配才能发生命中。
您可以使用hashmap_iterate
函数迭代存储在 hashmap 中的所有元素:
static int iterate ( void * const context , void * const value ) {
// If the value is 42...
if ( 42 == * ( int * ) value ) {
// Store into our user-provided context the value.
* ( void * * ) context = value ;
// Return 0 to tell the iteration to stop here.
return 0 ;
}
// Otherwise tell the iteration to keep going.
return 1 ;
}
int * value ;
if ( 0 != hashmap_iterate ( & hashmap , iterate , & value )) {
if ( * value != 42 ) {
// error!
}
} else {
// if we got here it means we never found 42 in the hashmap
}
您可以通过从回调函数返回非零来提前退出哈希图的迭代 - 也许您想处理并删除哈希图中的所有元素或仅搜索特定值。否则,如果回调返回零,则迭代将包含整个哈希图。
在某些应用程序中,例如需要打印哈希图的内容,除了值之外,您还需要访问键和键长度。为此,添加了第二个迭代器,称为hashmap_iterate_pairs
。
此外,从回调函数返回 -1 允许自动删除当前项目。当将动态分配的对象存储到映射并需要在销毁映射时释放内存时,这尤其方便。
int log_and_free_all ( void * const context , struct hashmap_element_s * const e ) {
int counter ;
for ( counter = 0 ; counter < e -> key_len ; counter ++ ) {
fputc ( e -> key [ counter ], ( FILE ) context );
}
fprintf (( FILE ) context , "=%s pair has been freedn" , ( char * ) e -> data );
free ( e -> data );
return -1 ;
}
void shut_down () {
if ( 0 != hashmap_iterate_pairs ( & hash , log_and_free_all , ( void * ) log )) {
fprintf ( stderr , "failed to deallocate hashmap entriesn" );
}
fclose ( log );
hashmap_destroy ( & hash );
}
要获取已放入哈希映射的条目数,请使用hashmap_num_entries
函数:
unsigned num_entries = hashmap_num_entries ( & hashmap );
要获取 hashmap 中分配的实际桶数(容量),请使用hashmap_capacity
函数:
unsigned num_entries = hashmap_capacity ( & hashmap );
要在使用完哈希图后销毁它,请使用hashmap_destroy
函数:
hashmap_destroy ( & hashmap );
这段代码几乎完全是由令人敬畏的 Pete Warden 编写的,基于 Elliott Back 现已不复存在的一篇博客文章。作者应用了以下进一步的更改:
这是发布到公共领域的免费且不受阻碍的软件。
任何人都可以出于任何目的(商业或非商业)以任何方式以源代码形式或编译的二进制文件自由复制、修改、发布、使用、编译、销售或分发本软件。
在承认版权法的司法管辖区,本软件的作者将软件中的任何及所有版权权益奉献给公共领域。我们做出这种奉献是为了广大公众的利益,而不是为了我们的继承人和继任者的利益。我们希望这种奉献成为永久放弃版权法规定的本软件所有当前和未来权利的公开行为。
本软件按“原样”提供,不提供任何明示或暗示的保证,包括但不限于适销性、特定用途的适用性和不侵权的保证。在任何情况下,作者均不对因本软件或本软件的使用或其他交易而产生或与之相关的任何索赔、损害或其他责任负责,无论是合同行为、侵权行为还是其他行为。
欲了解更多信息,请参阅http://unlicense.org/