1.5 字典

字典(Dictionary)与数组是程序设计语言的两个台柱子,这两种数据结构不但以自己的形象高频出镜,而且我们可以利用他们模拟出更多的数据结构,例如栈、链表、堆、以及各种队列等等。

1、什么是字典

我们在生活当中不可避免的要使用 key : value 这种键值对的方式来描述信息,比方说我们的成绩单,用科目名称作为key,成绩作为value。

                    成绩单
            -------------------
                “语文” : 60
                “数学” : 61
                “英语” : 60

这种需求是如此的频繁出现,所以编程语言责无旁贷的需要为我们提供一种数据结构来描述这种信息,这种数据结构就是字典(Dictionary)。

2、字典与数组的区别

数组是通过下标(索引号)来访问数组元素的,但数组的下标只是一个连续递增的数字,因为数字无法表达逻辑上的意义,因此这就显示了数组的一个缺陷:数组不擅长处理数据查询。比方说我们创建一个数组。

var array = ["a","b","c","d"]

如果我们想判断“f“是否在数组array中,那我们就不得逐一遍历数组的每一个元素才能知道(也就是说时间复杂度是o(n))。字典也使用下标(key)来访问数据,与数组下标的区别是,字典的下标可以表达出逻辑意义,比方说,我们如果在字典中查找语文成绩,那么字典完全有能力在不遍历所有元素的情况下给出答案(时间复杂度为o(1))。这就是字典与数组之间的重要区别。

3、在swift中创建字典

在swift中,字典也是通过struct来来定义的,所以我们可以这样来创建一个字典。

var dict = Dictionary<String,Int>()

也许这是字典最庄重的创建方式了吧,我们其实可以简单一点。

var dict = [String:Int]()

或者干脆,我们直接这样

var dict = ["语文":60,"数学":61,"英语":60]

这种方法比前两中方法更简洁,并且利用了类型推断机制,让编译器判定dict的类型。那么编译器是如何判定dict的类型的呢?和数组的类型一样,字典的类型也是由元素的类型所决定的,但字典元素的类型比数组的类型复杂,我们可以看到元素 "语文":60 其实是由两种类型构成 String:Int,所以dict的类型被判定为 [String:Int] 类型 。我们也可以用显示声明类型的方式定义字典。

var dict: [String:Int] = ["语文":60,"数学":61,"英语":60]

4、字典的实现原理

字典是在数组的基础之上构建起来的,在每个字典中都包含着一个数组。数组元素的定义顺序与存储循序是一致的。比方说我们创建一个数组。

var array = ["a","b","c","d"]

array元素的定义顺序为["a","b","c","d"],那么array的存储顺序也一定为["a","b","c","d"]。但如果我们创建一个字典。

var dict: [String:Int] = ["语文":60,"数学":61,"英语":60]

字典元素的定义顺序是["语文":60,"数学":61,"英语":60],那么字典元素的存储顺序和定义顺序是一致的吗?答案是: 不一定。原因是字典使用了一种散列机制。

在字典在存储元素 "语文":60 之前,字典首先对元素的key “语文” 进行一次hash运算,hash运算的目的是将key转换为一个Int类型的整数,比方说3,这个3就是元素将要在数组中的存放位置。同理,字典会对每一个将要存放的元素都会进行hash运算,以确定该元素的存放位置。字典正是凭借这种散列机制,获得了高效访问数据元素的能力。

5、字典的管理

字典的管理功能,都是围绕着字典元素的key展开的。理论上hash运算要达到两个目的,首先是不同的key经过hash运算后,会得到不同散列值(元素的存放位置);其次相同的key经过相同的hash运算会得到相同的散列值。

* 字典元素的访问
var dict: [String:Int] = ["语文":60,"数学":61,"英语":60]
dict["语文"]  // 60

我们使用元素的key来实现对字典元素的访问。当使用“语文”作为下标,进行成绩的查询时,字典首先会对“语文”进行hash运算,这样我们会再次得到散列值3,也就是元素 "语文":60 的存储位置。之后字典会用“语文”这个key,与3位置对应元素的key进行比较,如果确实相同,那么就会返回60。这样字典就实现了用o(1)的时间复杂度查找元素。如果我们访问的元素不存在,比方说我们插叙了“体育”成绩,swift字典的处理方式可以看另外一篇内容swift的Optional类型

* 字典元素修改与新增
dict["语文"] = 70

修改字典元素的value与对变量赋值操作并没有什么区别。除了修改之外,我们可以给字典新增元素,比方说我们现在新增一门体育成绩。

dict["体育"] = 60

这个新增操作确实令我们感觉有一点奇怪,字典并没有像数组那样提供一个append方法来新增元素,而是用了一种和修改操作同样的方式来新增元素,那么字典是如何判定一个操作是新增还是修改呢?当我们使用key来访问字典元素,并用=号进行赋值操作的时候,字典首先对key进行hash运算,如果得到的散列值对应的存储位置没有被其他元素占用,那么字典会将当前操作判定为新增操作。

如果得到散列值对应的存储位置上已经有元素了,那么字典会进一步判断当前的key与对应位置元素的key是否是相等的,如果是相等的,那么则判定为修改操作。新增与修改的融合,新增也是一种修改,修改也是一种新增。

* 什么是hash冲突

理论上,不同的key经过hash运算后会得到不同的散列值,但是hash算法总是会有自己的局限性,实际上不同的key经过相同hash运算后很有可能会产生相同的散列值,我们把这种现象称为hash冲突。正是因为hash冲突时无法避免的,所以,所有的字典都会提供对应的解决方案。通常的方案是,字典使用链表,将冲突的元素保存在同一个散列值指定的位置。当发生hash冲突的时候,字典的访问效率会降低,因为一但在一个存储位置产生冲突,那么字典就不得不在这个位置逐个访问每一个元素,才能确定哪一个才是真正要查询的元素。如果用一个极端的例子,如果字典中只有一个存储位置,那么所有的字典元素将会以链表的形式存储在这个唯一的位置上,从而使字典退化成为一个数组。

* 字典管理方法小结

如果我们需要删除一个字典元素,我们可以这样做

var dict: [String:Int] = ["语文":60,"数学":61,"英语":60]
dict.removeValueForKey("语文") // 语文key对应的元素被从字典中删除

当然,我们可以使用更简单的方法

dict["语文"] // 和removeValueForKey方法产生相同的效果

我们可以统计字典元素中的个数,以及判断一个字典是否为空

dict.count        // 统计当前字典中元素的个数
dict.isEmpty      // 判断字典是否为空

我们还可以把字典中的 key与value分别转换为数组

var keyArray = [String](dict.keys)
var valueArray = [Int](dict.values)

对于初学者来说,字典的操作方法并不值得我们去特意记忆,我们只需要有个印象,并在需要的时候查阅文档就可以了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容