hashmap哈希map是什么?什么时候需要使用hashmap?C实现hashmap示例

news2024/11/24 14:43:46

背景

对于C程序员,尤其是嵌入式C程序员,hashmap使用的相对较少,所以会略显陌生,hashmap其实涉及到2个概念,分别是哈希(hash)、map。

哈希hash:是把任意长度输入通过蓝列算法变换成固定长度的输出,这种转换是一种压缩映射,一般不可逆。这是专业的解释,我们常说的crc16、crc32、lrc、md5、sha256等,本质上都是哈希hash,只不过散列算法不同而已。

map: 是高级语言(比如java)中的一种数据结构,对于C语言,我们常用的就是数组,而map则是比数组更加高级的数据结构,其内部一般是数组+链表+红黑树的数据结构。如下图所示:

在这里插入图片描述

什么时候需要使用hashmap

既然hashmap是一种数据结构,我们不妨从常用的数据结构对比分析:

数据结构类型存储形式检索方式
数组空间大小固定,要存的数据有唯一的、递增/连续的索引值通过数组索引值检索
链表空间动态扩展,要存储的数据有唯一的、不需要联系的索引或key值遍历链表
数据库空间动态扩展,要存储的数据有唯一的key值通过SQL语句 or key值检索

上面的数据结构是我们最常用的集中数据格式,对于C程序员,常用的是数组和链表,而数据库由于相对较重,用的并不会太多。那么数组和链表的特点是什么呢?

  1. 数组的大小不能够动态扩展, 需要唯一的索引值,而且索引值最好是递增连续,但是数组的查找效率是最高的。
  2. 链表的大小可以动态扩展,也需要一个唯一的key值,不需要key是连续或递增,但是查找的效率是最低的,因为每次都是需要遍历查找。

那有没有一种数据结构,能够兼顾查找效率和动态扩展呢?答案是有,hashmap就是。hashmap可以存放无序的数据,查找的效率相比链表提升非常多。我们可以简单的认为hashmap是一种轻量级、极其简单的内存数据库(类似redis的实现原理也涉及hashmap),当我们的代码中需要存储大量的动态扩展的数据节点时,并且会频繁查询时,在不引入数据库的前提下,hashmap相比链表是更合适的一种数据结构。

hashmap 和 链表 遍历查询效率比较

完整的测试代码已经上传到gitee,地址如下:
C语言实现hashmap

测试代码 hash_list_cmp_test.c

/*
 * A unit test and example of how to use the simple C hashmap
 */

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <sys/time.h>
#include <stdint.h>
#include <string.h>
#include "hashmap.h"
#include "list.h"

#define KEY_MAX_LENGTH (256)
#define KEY_PREFIX ("somekey")
#define KEY_COUNT (300)

typedef struct data_struct_s
{
    char key_string[KEY_MAX_LENGTH];
    int number;
} data_struct_t;

typedef struct{
    char key_buf[KEY_MAX_LENGTH];
    int value;
    struct slist_s list;
} data_list_node_t;

struct slist_s g_data_list_head;

int main(char* argv, int argc)
{
    int index, i;
    int error;
    map_t mymap;
    char key_string[KEY_MAX_LENGTH];
    data_struct_t* value;

    data_list_node_t *node = NULL;

    struct timeval tv1, tv2;
    uint64_t t1, t2;

    mymap = hashmap_new();

    slist_init(&g_data_list_head);

    /* First, populate the hash map with ascending values */
    for (index=0; index<KEY_COUNT; index+=1){
        /* Store the key string along side the numerical value so we can free it later */
        value = malloc(sizeof(data_struct_t));
        snprintf(value->key_string, KEY_MAX_LENGTH, "%s%d", KEY_PREFIX, index);
        value->number = index;
        error = hashmap_put(mymap, value->key_string, value);
        assert(error==MAP_OK);

        // list node add
        node = (data_list_node_t *)malloc(sizeof(data_list_node_t));
        memset(node, 0, sizeof(data_list_node_t));
        slist_init(&node->list);

        snprintf(node->key_buf, KEY_MAX_LENGTH, "%s%d", KEY_PREFIX, index);
        node->value = index;

        slist_add_tail(&node->list, &g_data_list_head);
    }

    /* Now, check all of the expected values are there */
    gettimeofday(&tv1, NULL);
    for (index=0; index<KEY_COUNT; index+=1){
        snprintf(key_string, KEY_MAX_LENGTH, "%s%d", KEY_PREFIX, index);
        error = hashmap_get(mymap, key_string, (void**)(&value));
        
        /* Make sure the value was both found and the correct number */
        assert(error==MAP_OK);
        assert(value->number==index);
	
	    //printf("get key:%s, value:%d\n", value->key_string, value->number);

    }
    gettimeofday(&tv2, NULL);
    t1 = (uint64_t)tv1.tv_sec * 1000000 + tv1.tv_usec;
    t2 = (uint64_t)tv2.tv_sec * 1000000 + tv2.tv_usec;
    printf("hash map iterate cost time %ld us\n", t2 - t1);


    gettimeofday(&tv1, NULL);
    for(i = 0; i < KEY_COUNT; i++){
        snprintf(key_string, KEY_MAX_LENGTH, "%s%d", KEY_PREFIX, i);
        slist_for_each_entry(&g_data_list_head, node, data_list_node_t, list){
            if(0 == strcmp(node->key_buf, key_string)){
          //      printf("find %s ok, value=%d\n", node->key_buf, node->value);
                break;
            }
        }

    }
    gettimeofday(&tv2, NULL);
    t1 = (uint64_t)tv1.tv_sec * 1000000 + tv1.tv_usec;
    t2 = (uint64_t)tv2.tv_sec * 1000000 + tv2.tv_usec;
    printf("list iterate cost time %ld us\n", t2 - t1);
 
    /* Now, destroy the map */
    hashmap_free(mymap);

    return 1;
}

运行结果:

hash map iterate cost time 60 us
list iterate cost time 435 us

从上述结果可知,hashmap的查找效率要比链表的查找效率高7倍以上。

小结

  1. hashmap是一种数据结构,跟数组、链表同级别的数据结构。
  2. hashmap有数组和链表的优点,查找效率比链表高,动态可扩展性比数组好。
  3. hashmap是用空间、算法来实现的一种内部轻量级数据结构,我们在开发程序时,如果涉及频繁查找,可以使用hashmap替换链表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS Flex 布局的 flex-direction 属性讲解

flex-direction 设置了主轴&#xff0c;从而定义了弹性项目放置在弹性容器中的方向。 Flexbox 是一种单向布局概念&#xff0c;可将弹性项目视为主要以水平行或垂直列布局。 .container {flex-direction: row | row-reverse | column | column-reverse; }几种支持的属性&#x…

devServer和VueCli | Webpack

文章目录devServer和VueCliwebpack-dev-servercontentBase模块热替换开启HMRhotOnly host配置port open compressproxyresolveextensions和alias配置如何区分开发环境devServer和VueCli webpack-dev-server contentBase 模块热替换 开启HMR hotOnly host配置 port open compres…

IMX6ULL学习笔记(15)——GPIO接口使用【官方SDK方式】

一、GPIO简介 i.MX6ULL 芯片的 GPIO 被分成 5 组,并且每组 GPIO 的数量不尽相同&#xff0c;例如 GPIO1 拥有 32 个引脚&#xff0c; GPIO2 拥有 22 个引脚&#xff0c; 其他 GPIO 分组的数量以及每个 GPIO 的功能请参考 《i.MX 6UltraLite Applications Processor Reference M…

有没有股票实时行情的同花顺l2接口?

对于个人投资者而言&#xff0c;一般看盘平台软件系统中&#xff0c;通过自定义公式接口&#xff0c;可以获取到股票实时行情的。例如同花顺l2接口系统会通过一些输入的公式就能查找指定的股票行情了&#xff0c;那么这个就相当于股票实时行情的API接口一样的道理&#xff0c;输…

加密解决HTTP协议带来的安全问题

HTTP协议默认是采取明文传输的&#xff0c;容易被中间人窃听、拦截、篡改&#xff0c;存在安全隐患。 常见提高安全性的方法是对通信内容进行加密&#xff0c;再进行传输&#xff0c;常见的加密方式有 不可逆加密&#xff1a;单向散列函数 可逆加密&#xff1a;对称加密、非对称…

从模型到服务——iDesktopX处理自动化工具实现BIM模型到三维服务发布

目录前言一、 处理自动化模型二、 算子参数设置1、 使用迭代数据集打开导出后的BIM模型2、 移除重复点、重复面和重复子对象3、 模型生成缓存4、 三维切片缓存发布5、 执行结果前言 BIM模型在SuperMap实际使用的业务流程中常常需要在桌面产品中生成缓存&#xff0c;然后通过iS…

安装和配置MySQL

首先前往官网下载mysql社区版&#xff08;不要钱&#xff09; MySQL Community Serverhttps://dev.mysql.com/downloads/windows/installer/8.0.html 甲骨文比较鸡贼&#xff0c;会要求你注册一个账号。但是下面有一行小字&#xff0c;直接点击下载就好了 双击后直接按…

Blender——苹果的纹理绘制

效果图 前言 在进行纹理绘制之前&#xff0c;首先要具有苹果三维模型。 关于苹果的建模请参考&#xff1a;Blender——“苹果”建模_行秋的博客 1.苹果UV的展开 1.1首先点击UV Eidting&#xff0c;滑动三维模型&#xff0c;使其大小适中。 1.2打开左上角的UV选区同步&#x…

使用idb操作IndexedDB

使用idb操作IndexedDB 译自&#xff1a;https://www.hackernoon.tech/use-indexeddb-with-idb-a-1kb-library-that-makes-it-easy-8p1f3yqq GitHub地址&#xff1a;https://github.com/jakearchibald/idb 文章目录使用idb操作IndexedDB前置条件本文承诺上手demo1&#xff1a;…

python中的encode()和decode()函数

前言&#xff1a; 我们知道&#xff0c;计算机是以二进制为单位的&#xff0c;也就是说计算机只识别0和1,也就是我们平时在电脑上看到的文字&#xff0c;只有先变成0和1&#xff0c;计算机才会识别它的意思。这种数据和二进制的转换规则就是编码。计算机的发展中&#xff0c;有…

【SpringCloud Alibaba】Sentinel流控规则

概念 流控规则 直接&#xff08;默认&#xff09; QPS快速失败 线程数直接控制 QPSWarming up QPS排队等待 关联 链路 具体启动Sentinel的步骤可以参考我的上一篇文章。 概念 资源名&#xff1a;唯一名称&#xff0c;默认请求路径 针对来源&#xff1a;Sentinel可以针…

微服务系列之远程服务调用

随笔 对自己不满是任何有才能的人的根本特征 参考书籍&#xff1a; “凤凰架构”“微服务架构设计模式” 本篇文章开始之前提示一下&#xff0c;读者带着“IPC与RPC的有什么区别”疑惑读效果更好 引言 从架构师的角度来看&#xff0c;微服务架构的落地实现第一个需要解决问…

改良型新药之详细分类

随着一类新药开发越来越困难、仿制药竞争激烈&#xff0c;改良型新药被认为符合我国医药企业转型升级的方向&#xff0c;吸引了更多企业切入&#xff0c;本文也将针对改良型新药的6个常见共性问题给予解答&#xff0c;涉及科普、专利、分类、临床价值、立项、注册申请、数据统计…

windows@网络防火墙@软件联网控制

文章目录ref打开防火墙控制面板常用部分限制某个软件联网文档参考具体操作取消控制/禁用第三方软件控制ref (Windows) 创建出站端口规则 | Microsoft LearnWindows Defender Firewall with Advanced Security (Windows) | Microsoft Learn组策略 Windows) 高级安全性的 Window…

你可能不知道的DOM断点调试技巧

前言 作为一个前端&#xff0c;DOM断点应该是我们非常熟悉的&#xff0c;也是我们日常工作中经常要用到的一种调试技巧&#xff1b;但是下面这些DOM断点调试技巧你可能不知道&#xff0c;且听我一一道来。 监听元素 有这样一种场景&#xff0c;当DOM中某个元素移除或者元素属…

再学C语言14:基本运算符

C使用运算符&#xff08;operator&#xff09;代表算数运算 一、赋值运算符&#xff08;assignment operator&#xff09;&#xff1a; 在C中&#xff0c;符号并不表示“相等”&#xff0c;而是一个赋值运算符 year 2022; 符号左边是一个变量名&#xff0c;右边是赋给该变…

arraybuffer的应用,下载图片/文件等

在这篇文章中&#xff0c;我们了解了js中arraybuffer是用来存储二进制缓存的&#xff0c;但是都是理论知识&#xff0c;本篇文章来介绍一个arraybuffer应用的场景。 主要应用场景是下载文件&#xff0c;在ajax请求中&#xff0c;设置responseType arraybuffer 得到一个二进制…

零刻 SEi12 Pro,ALL IN ONE搭建教程

一台mini的NUC能做什么&#xff1f;当然每个人的心里都会有着不同的答案&#xff0c;既然是一台Mini主机那就肯定少不了部署一个All-In-One来榨干他的性能。今天我就大家带来一个部署All-In-One的详细教程&#xff0c;希望能够对大家有帮助。 我这台机器配置是i5-1240P 16G内存…

【mpvue】mpvue-echarts echarts动态渲染、延迟加载、双轴动态计算、双轴对齐

mpvue-echarts 双轴折线案例使用echarts双轴折线图实战项目导入一、打包结果超过小程序大小限制&#xff1f;1.下载自定义echarts.js2. 引入 echarts.js![在这里插入图片描述](https://img-blog.csdnimg.cn/ff4ad6d894404e97bceff0581fc1f736.png#pic_center)3. 项目引入二、图…

蓝桥杯备赛Day4——多维数组

二维数组初始化 p[[0 for i in range(5)] for j in range(2)] #法一 p[[0]*5 for j in range(2)] #法二 s[[1,2,3],[4,5,6]] print(s) for i in range(2):for j in range(3):print(s[i][j],end ) 三维数组初始化 a[[[0 for _ in range(2)] for __ in…