背景介绍

最近使用Python开发项目为主,当使用到字典时感觉非常方便实用。那么好奇心就驱使我要搞清楚字典是怎么实现的。为了真正的搞清楚字典的实现就不得不使用C语言来实现一遍,为此我查了一些资料现在总结一下。

字典简述

字典也被称为关联数组,还称为哈希数组等。实现的原理一般是有一个键值对,通过键可以索引到值。很多工具都使用这种方式保存数据,例如redis/memcached/mongo等。所以键是唯一的,要是实现字典的快速查询肯定不能使用字符串遍历对比的方式实现。那样实现的字典会非常非常的慢。我们都知道在数组中使用下标索引可以快速的得到对应值,所以我们需要做的就是怎样把键计算出一个唯一值并且这个值要唯一还要在我们的数组索引值范围呢。举例子说,当一个键为hello的时候,我的数组最大索引是1024这个范围呢!我们的键值对可以有1024对。那么如果按照ascii值对hello进行一个简单的加法计算我们会得到什么呢?下面运行一段c程序看看。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
sum+= key[i];
}
return sum; } int main(int argc, char * argv[]){
int key_id = sum_ascii("hello");
printf("The key id %d\n", key_id);
return 0;
}

看到这个532你会觉得正好,至少没有超过1024这个索引范围,可以用了。不要着急,我们继续将hello改成hellohashtable看看会得到多少值。



糟糕了!超出索引范围了,你可能会想到两种解决方案。

*1,增加数组大小。

*2,写一篇说明书,说明你的这个键不能超过多少个字符。

这两个方案显然是无法让使用者开心的,所以我们需要解决掉。我们可以使用下面的方式获取绝对不会超过索引范围的值,猜猜什么办法。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define SIZE_ARRAY 1024 int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
sum+= key[i];
}
return sum; } int residue(int size){
return size % SIZE_ARRAY;
} int main(int argc, char * argv[]){
int key_id = sum_ascii("hellohashtable");
key_id = residue(key_id);
printf("The key id %d\n", key_id);
return 0;
}

现在好了,我们不会超出索引范围了。那么问题又来了,我们的计算值很明显非常容易出现冲突,下面举一个例子吧。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define SIZE_ARRAY 1024 int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
sum+= key[i];
}
return sum; } int residue(int size){
return size % SIZE_ARRAY;
} int main(int argc, char * argv[]){
char * key1 = "hellolandpack";
char * key2 = "hellohellohellohelloak6";
int key1_id = sum_ascii(key1);
int key2_id = sum_ascii(key2);
key1_id = residue(key1_id);
key2_id = residue(key2_id);
printf("The key[%s] id %d\n", key1,key1_id);
printf("The key[%s] id %d\n", key2,key2_id);
return 0;
}



可以看到,我们的key显然是不同的,但是却生成了同样的一个索引值。这就会导致数据冲突了。下面换一种方式计算键值。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define SIZE_ARRAY 1024 int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
//sum+= key[i];
sum = sum << 8;
sum += key[i];
}
return sum; } int residue(int size){
return size % SIZE_ARRAY;
} int main(int argc, char * argv[]){
char * key1 = "hellolandpack";
char * key2 = "hellohellohellohelloak6";
int key1_id = sum_ascii(key1);
int key2_id = sum_ascii(key2);
key1_id = residue(key1_id);
key2_id = residue(key2_id);
printf("The key[%s] id %d\n", key1,key1_id);
printf("The key[%s] id %d\n", key2,key2_id);
return 0;
}



难道这样就不会生成重复的键值吗?不可能哈,来看看下面这个测试。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define SIZE_ARRAY 1024 int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
//sum+= key[i];
sum = sum << 8;
sum += key[i];
}
return sum; } int residue(int size){
return size % SIZE_ARRAY;
} int main(int argc, char * argv[]){
//char * key1 = "hellolandpack";
//char * key2 = "hellohellohellohelloak6";
char * key1 = "ddddd";
char * key2 = "dddd";
int key1_id = sum_ascii(key1);
int key2_id = sum_ascii(key2);
key1_id = residue(key1_id);
key2_id = residue(key2_id);
printf("The key[%s] id %d\n", key1,key1_id);
printf("The key[%s] id %d\n", key2,key2_id);
return 0;
}

好了,问题又来了!我们又遇到问题了。这个和之前的算法一样无法解决唯一键值问题。那么干嘛还要引入这个呢?来再看看原来的算法。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define SIZE_ARRAY 1024 int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
//sum+= key[i];
//sum = sum << 8;
sum += key[i];
}
return sum; } int residue(int size){
return size % SIZE_ARRAY;
} int main(int argc, char * argv[]){
//char * key1 = "hellolandpack";
//char * key2 = "hellohellohellohelloak6";
char * key1 = "hello";
char * key2 = "olleh";
int key1_id = sum_ascii(key1);
int key2_id = sum_ascii(key2);
key1_id = residue(key1_id);
key2_id = residue(key2_id);
printf("The key[%s] id %d\n", key1,key1_id);
printf("The key[%s] id %d\n", key2,key2_id);
return 0;
}

好了!可以看到。对于同一组字符的不同顺序它居然认为是同一个键,显然是错误的。而增加了向右移位处理后我们可以得到如下的结果。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define SIZE_ARRAY 1024 int sum_ascii(char * key){
unsigned long sum = 0;
unsigned long i;
for(i = 0; i < strlen(key);i++){
//sum+= key[i];
sum = sum << 8;
sum += key[i];
}
return sum; } int residue(int size){
return size % SIZE_ARRAY;
} int main(int argc, char * argv[]){
//char * key1 = "hellolandpack";
//char * key2 = "hellohellohellohelloak6";
char * key1 = "hello";
char * key2 = "olleh";
int key1_id = sum_ascii(key1);
int key2_id = sum_ascii(key2);
key1_id = residue(key1_id);
key2_id = residue(key2_id);
printf("The key[%s] id %d\n", key1,key1_id);
printf("The key[%s] id %d\n", key2,key2_id);
return 0;
}

总结

现在大概了解了一下设计字典最大的难点就是怎样去容纳各种不同的键值,并且产生一个唯一的索引。下一章我将会具体实现一个字典,并且在最后我会将其编译为一个python包。

如果你在使用字典或者键值数据库有什么有趣的发现就快快分享分享吧~~

Python字典实现分析的更多相关文章

  1. Python文章相关性分析---金庸武侠小说分析

    百度到<金庸小说全集 14部>全(TXT)作者:金庸 下载下来,然后读取内容with open('names.txt') as f: data = [line.strip() for li ...

  2. 用python探索和分析网络数据

    Edited by Markdown Refered from: John Ladd, Jessica Otis, Christopher N. Warren, and Scott Weingart, ...

  3. python爬虫之分析Ajax请求抓取抓取今日头条街拍美图(七)

    python爬虫之分析Ajax请求抓取抓取今日头条街拍美图 一.分析网站 1.进入浏览器,搜索今日头条,在搜索栏搜索街拍,然后选择图集这一栏. 2.按F12打开开发者工具,刷新网页,这时网页回弹到综合 ...

  4. Python字典 你必须知道的用法系列

    本文Python版本为3.7.X,阅读本文之前需了解python字典的基本用法. 介绍 字典(dict)是Python中内置的一个数据结构,由多个键值对组成,键(key)和值(value)用冒号分隔, ...

  5. Python文章相关性分析---金庸武侠小说分析-2018.1.16

    最近常听同事提及相关性分析,正巧看到这个google的开源库,并把相关操作与调试结果记录下来. 输出结果: 比较有意思的巧合是黄蓉使出打狗棒,郭靖就用了降龙十八掌,再后测试了名词的解析. 小说集可以百 ...

  6. python字典推导式 - python基础入门(17)

    在昨天的文章中,我们介绍了关于python列表推导式 的使用,字典推导式使用方法其实也类似,也是通过循环和条件判断表达式配合使用,不同的是字典推导式返回值是一个字典,所以整个表达式需要写在{}内部. ...

  7. 自己动手实现 HashMap(Python字典),彻底系统的学习哈希表(上篇)——不看血亏!!!

    HashMap(Python字典)设计原理与实现(上篇)--哈希表的原理 在此前的四篇长文当中我们已经实现了我们自己的ArrayList和LinkedList,并且分析了ArrayList和Linke ...

  8. Python字典和集合

    Python字典操作与遍历: 1.http://www.cnblogs.com/rubylouvre/archive/2011/06/19/2084739.html 2.http://5iqiong. ...

  9. python 字典排序 关于sort()、reversed()、sorted()

    一.Python的排序 1.reversed() 这个很好理解,reversed英文意思就是:adj. 颠倒的:相反的:(判决等)撤销的 print list(reversed(['dream','a ...

随机推荐

  1. for循环中的占位 pass

  2. .NET各大平台数据列表控件绑定原理及比较(WebForm、Winform、WPF)

    说说WebForm: 数据列表控件: WebForm 下的列表绑定控件基本就是GridView.DataList.Repeater:当然还有其它DropDownList.ListBox等. 它们的共同 ...

  3. ABP理论学习之数据过滤器

    返回总目录 本篇目录 介绍 预定义过滤器 关闭过滤器 开启过滤器 设置过滤器参数 定义自定义过滤器 其他ORM 介绍 软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除 ...

  4. ReactJS入门(二)—— 组件的生命周期

    如果你熟悉avalon,使用过 data-include-rendered 和 data-include-loaded 等回调方法,那么你会很好地理解React组件的各个生命周期. 说白了其实就是Re ...

  5. percona5.7 源码安装

    200 ? "200px" : this.width)!important;} --> 介绍 主要为了测试percona的线程池的性能,这里就简单介绍一下percona5.7 ...

  6. 配置NHibernate将枚举保存为Oracle数据库中的字符串

    假设有这样一个枚举: /// <summary> /// 字典项类型 /// </summary> public enum DicItemType { [EnumDescrip ...

  7. lua中实现异步资源读写

    同样还是更新方面的需求,当我们检测到版本是新安装的以后,要进行upd目录清除.如果使用os.execute执行 rm -rf ooxx 是非常快的但由于os.execute一旦报错,那整个lua进程就 ...

  8. 我如何介绍 Microservice

    这篇文章转自我的 Github blog 一天我司招财猫姐(HR 大人)问我,你给我解释一下 Microservice 是什么吧.故成此文.一切都是从一个创业公司开始的. 故事 最近的创业潮非常火爆, ...

  9. Atitit wsdl的原理attilax总结

    Atitit wsdl的原理attilax总结 1.1. 在 W3C 的 WSDL 发展史1 1.2. 获取wsdl,可能需要url后面加wsdl,也可能直接url1 1.3. Wsdl的作用2 1. ...

  10. 谈谈关键字final

    final:可用于修饰类.方法.变量,表示它修饰的类.方法和变量不可改变. (1)修饰变量:变量只能被赋值一次,赋值后不能更改.按照Java代码惯例,final变量就是常量,而且通常常量名要大写: ① ...