介绍Erlangdict模块( dictionary),dict就是一个通过散列(hash)来存放数据的组织方式,同时dict模块还提供了完整的操作接口,类似的模块还有orddict模块。具体讲如何使用dict模块的各种常用方法之前,先来看一些基础的概念。

   大家写程序如果有一段时间了,发现程序中数组和链表是使用频率很高的两种数据结构,到底如何选择全看实际应用,这里只给出数组和链表的一些基本特性的简要说明:数组进行元素查找操作很快,但是增加删除元素较慢;链表增加删减元素很快,但是查找较慢,具体的原因根据它们的存贮结构就可以看出来了。下面引用一张来自java数据结构和算法》中图,可以给大家在选择数据结构时一个很好的参考和指南:

 

数据存贮结构选择

 

散列结构同时兼备数组的快速查询和链表的快速增减的,erlangdict模块就提供了这种散列机制,这里需要注意的是,Erlang dictionary和进程字典(process dic)一样,只在节点内的一个进程内使用的,而ets可以在多个进程中共享使用(当然你也可以把ets设定为不可以让其他进程使用),下面开始看dict模块具体使用。

建立一个字典,并存储一对key-value,燃火根据key查询值:

 
  
  1. 6> D = dict:new(). 
  2. {dict,0,16,16,8,80,48, 
  3.       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, 
  4.       {{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}} 
  5. 7> D1 = dict:store(lang,zh_cn, D). 
  6. {dict,1,16,16,8,80,48, 
  7.       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, 
  8.       {{[],[],[],[],[],[],[], 
  9.         [[lang|zh_cn]], 
  10.         [],[],[],[],[],[],[],[]}}} 
  11. 8> dict:fetch(lang,D1).            
  12. zh_cn 

现在字典D1中就已经存放了一对key-valuelang->zh_cn,这里有个重要的特性需要指出就是字典可以作为参数在函数间传递。Dict模块的使用过程中,字典都是作为参数传递给函数,然乎返回一个新的字典,这其实就是Erlang 作为一种FP语言的一种特性---为了安全,保证变量不变。

接下来我们向字典中存贮一些更复杂的内容,比如存放一个列表:

 
  
  1. 17> D2 = dict:store(langs,[en,de],D1). 
  2. {dict,2,16,16,8,80,48, 
  3.       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, 
  4.       {{[],[],[], 
  5.         [[langs,en,de]], 
  6.         [],[],[], 
  7.         [[lang|zh_cn]], 
  8.         [],[],[],[],[],[],[],[]}}} 
  9. 18> D3 = dict:append(langs,jp,D2). 
  10. {dict,2,16,16,8,80,48, 
  11.       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, 
  12.       {{[],[],[], 
  13.         [[langs,en,de,jp]], 
  14.         [],[],[], 
  15.         [[lang|zh_cn]], 
  16.         [],[],[],[],[],[],[],[]}}} 
  17. 19> D4 = dict:append_list(langs, [han,bz,af], D3). 
  18. {dict,2,16,16,8,80,48, 
  19.       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, 
  20.       {{[],[],[], 
  21.         [[langs,en,de,jp,han,bz,af]], 
  22.         [],[],[], 
  23.         [[lang|zh_cn]], 
  24.         [],[],[],[],[],[],[],[]}}} 
  25. 20> dict:fetch(langs,D4). 
  26. [en,de,jp,han,bz,af] 
  27. 21> dict:fetch(lang,D4).  
  28. zh_cn 
  29. 22> dict:find(langs,D4).  
  30. {ok,[en,de,jp,han,bz,af]} 

这里我们以langs这把key存贮一个由语言缩写构成的列表,并在接下来多添加了一种语言,然后又一次性添加了多种语言,最后进行查询测试。这些都很简单,但是有一点需要注意的是,从字典中查询key的值时,请使用find方法,查找成功返回{ok, values}这个tuple,失败返回 error,而fetch查找失败直接就报错了,如:

 
  
  1. 24> dict:fetch(langss,D4). 
  2. ** exception error: bad argument 
  3.      in function  dict:fetch/2 
  4.         called as dict:fetch(langss, 
  5.                              {dict,2,16,16,8,80,48, 
  6.                                    {[],[],[],[],[],[],[],[],[],[],[],[],[],[], 
  7.                                     [],[]}, 
  8.                                    {{[],[],[], 
  9.                                      [[langs,en,de,jp,han,bz,af]], 
  10.                                      [],[],[], 
  11.                                      [[lang|zh_cn]], 
  12.                                      [],[],[],[],[],[],[],[]}}}) 
  13. 25> dict:find(langss,D4).  
  14. error 

统计字典里面存贮了多少key:

 
  
  1. 35> dict:fetch_keys(D4).  
  2. [langs,lang] 
  3. 36> dict:size(D4).        

通过一个tuple-lists结构快速构建一个字典:

 
  
  1. 27> KV = [{name,["zhang"]},{fav, "lol"},{location,"gz"}]. 
  2. [{name,["zhang"]}},{fav,"lol"},{location,"gz"}] 
  3. 29> D = dict:from_list(KV). 
  4. {dict,3,16,16,8,80,48, 
  5.       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, 
  6.       {{[],[],[],[],[], 
  7.         [[name,108,117,111]], 
  8.         [[fav,108,111,108]], 
  9.         [],[],[],[],[],[],[], 
  10.         [[location,103,122]], 
  11.         []}}} 

   这样就初始化并且存放了值到一个字典,key-value结构和我们的KV结构一致,这里将name的值设定为一个列表,方便我们后期可以通过appendappend_list来扩展。

最后还有几个有用的函数,fold,filter,map,update,用法和作用和以前介绍列表操作里面的一致。

    好些文章和书籍都不推荐使用进程字典,理由是破坏了erlangFP原则,还有些朋友曾经和我说用了进程字典后,严重影响代码的可读性。我本人觉得该用还是得用,用的时候自己注意就好,毕竟这是Erlang里面速度最快的大数据结构了吧。如果对风格和安全要求很严格的话,又想兼顾散列这种优秀性能的话,那就使用dict模块吧。