Python字典实现分析
背景介绍
最近使用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字典实现分析的更多相关文章
- Python文章相关性分析---金庸武侠小说分析
百度到<金庸小说全集 14部>全(TXT)作者:金庸 下载下来,然后读取内容with open('names.txt') as f: data = [line.strip() for li ...
- 用python探索和分析网络数据
Edited by Markdown Refered from: John Ladd, Jessica Otis, Christopher N. Warren, and Scott Weingart, ...
- python爬虫之分析Ajax请求抓取抓取今日头条街拍美图(七)
python爬虫之分析Ajax请求抓取抓取今日头条街拍美图 一.分析网站 1.进入浏览器,搜索今日头条,在搜索栏搜索街拍,然后选择图集这一栏. 2.按F12打开开发者工具,刷新网页,这时网页回弹到综合 ...
- Python字典 你必须知道的用法系列
本文Python版本为3.7.X,阅读本文之前需了解python字典的基本用法. 介绍 字典(dict)是Python中内置的一个数据结构,由多个键值对组成,键(key)和值(value)用冒号分隔, ...
- Python文章相关性分析---金庸武侠小说分析-2018.1.16
最近常听同事提及相关性分析,正巧看到这个google的开源库,并把相关操作与调试结果记录下来. 输出结果: 比较有意思的巧合是黄蓉使出打狗棒,郭靖就用了降龙十八掌,再后测试了名词的解析. 小说集可以百 ...
- python字典推导式 - python基础入门(17)
在昨天的文章中,我们介绍了关于python列表推导式 的使用,字典推导式使用方法其实也类似,也是通过循环和条件判断表达式配合使用,不同的是字典推导式返回值是一个字典,所以整个表达式需要写在{}内部. ...
- 自己动手实现 HashMap(Python字典),彻底系统的学习哈希表(上篇)——不看血亏!!!
HashMap(Python字典)设计原理与实现(上篇)--哈希表的原理 在此前的四篇长文当中我们已经实现了我们自己的ArrayList和LinkedList,并且分析了ArrayList和Linke ...
- Python字典和集合
Python字典操作与遍历: 1.http://www.cnblogs.com/rubylouvre/archive/2011/06/19/2084739.html 2.http://5iqiong. ...
- python 字典排序 关于sort()、reversed()、sorted()
一.Python的排序 1.reversed() 这个很好理解,reversed英文意思就是:adj. 颠倒的:相反的:(判决等)撤销的 print list(reversed(['dream','a ...
随机推荐
- SQL Server 2016 CTP2.3 的关键特性
SQL Server 2016 CTP2.3 的关键特性 数据库方面的增强 Row Level Security已经支持In-memory OLTP 表.用户现在可以对内存优化表实施row-level ...
- 软件工程(QLGY2015)博客点评总结
目录 第一次作业(2015.5.9) 第二次作业(2015.5.21) 第三次作业(2015.6.11) 2015上半年软工助教总结 第一次作业(2015.5.9) 存在主要问题 1)书写这种练习博客 ...
- MySQL实现嵌套集合模型
MySQL实现嵌套集合模型 译文主要是介绍如何用MySQL来存储嵌套集合数据.在其中会增加一些自己的理解,也会删除掉一些自认为无用的废话. 这篇文章主要讲的是嵌套集合模型,所以邻接表不是本文的重点,简 ...
- 玩转动态编译 - 高级篇:一,IL访问静态属性和字段
IL介绍 通用中间语言(Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一种属于通用语言架构和.NET ...
- 如何创建一个AJAX-Enabled WCF Service
原创地址:http://www.cnblogs.com/jfzhu/p/4041638.html 转载请注明出处 前面的文章中介绍过<Step by Step 创建一个WCF Servi ...
- 《Hive编程指南》—— 读后总结
知识图谱
- Java易混淆的概率:成员变量、类变量、实例变量、局部变量
先看代码 public class Variable{ int b=0; //实例变量 static int a=0; //类变量 final String c="wws"; // ...
- 当GitHub把我当成DDos攻击者拉进了黑名单中。。。
Github黑名单自救+快速稳定FQ 异常处理汇总-开发工具 http://www.cnblogs.com/dunitian/p/4522988.html 原因:项目冲突,没有先更新后提交,他自行更 ...
- LINQ系列:LINQ to SQL Take/Skip
1. Take var expr = context.Products .Take(); var expr = (from p in context.Products select p) .Take( ...
- Qt on Android:创建可伸缩界面
使用 Qt 来开发 Android 应用,也需要适配不同移动设备,适配多种多样的屏幕和分辨率.这次我们大概来讲一下如何使用 Qt 提供的机制来创建可伸缩的界面. DPI 必须要解释一下 DPI . D ...