Swift 初学者 ( 11/12 ) 字典

简介

这是一个Swift语言教程,基于最新的iOS 9,Xcode 7.3和Swift 2.2,会为你介绍Swift编程非常基础的内容。从电脑如何工作的全程基本原理到语言结构,你会足够了解这门语言,来处理数据和管理代码的行为。

快速链接


字典

字典(dictionary)是没有顺序的一对一对的集合,每一对由一个键(key)和一个值(value)组成。

在上面的示意图可以看到,键是唯一的。相同的键不能在一个字典里出现两次,但不同的键可以指向相同的值。所有的键都要是相同的类型,所有的值也要是相同的类型。

当你想通过一个标识符查找值时,字典很有用。例如,这篇教程内容的表格映射了章节名到他们的链接,让你轻松地就可以快速跳到想阅读的章节。

这和数组有什么不同?用数组,你只能通过它的索引获取一个值,必须要是整数,并且所有索引都要是按顺序的。在一个字典里,键可以是任意类型,也不需要有特定的顺序。

创建字典

当你在创建字典的时候,你可以显示声明键的类型和值的类型。这是字典显示声明的例子:

let pairs: Dictionary<String, Int>

这和数组的显示声明相似,但这里的尖括号里有两个类型,用逗号间隔:键的类型和值的类型。这个例子里,paris 是一个不可以被修改的字典,字符串作为键,整数作为值。

隐式声明

Swift 也能从初始化的类型中推断出字典的类型:

let inferredPairs = Dictionary<String, Int>()

或者用更推荐的简写:

let alsoInferredPairs = [String: Int]()

在方括号里,键的类型后面跟着一个冒号,然后是值的类型。这是声明字典最常用的方式,你会在本教程里全程使用它。

字典字面值

如果你想用初始值声明一个字典,你可以使用字典字面值。这是用逗号间隔的键值对列表,包裹在方括号里。

对于上一篇教程里你的纸牌游戏,不需要使用两个数组来映射选手到他们的分数,用字典字面值就好了:

let namesAndScores = ["Anna": 2, "Brian": 2, "Craig": 8, "Donna": 6]
print(namesAndScores)
// > ["Brian": 2, "Anna": 2, "Craig": 8, "Donna": 6]

这个例子里,字典有一对对 [String: Int]。当你打印字典的时候,可以看到这几对并没有特殊的顺序。

空的字典字面值看起来像这样:[:]。你可以像这样使用它来把一个已经存在的字典清空:

var emptyDictionary: [Int: Int]
emptyDictionary = [:]

访问值

就像数组一样,有几种方式来访问字典的值。

使用下标

字典支持下标来访问值。不像数组,不是用索引来访问值,而是用它的键。例如你希望得到 Anna 的分数,就这样输入:

print(namesAndScores["Anna"])
// > Optional(2)

注意返回值是可选值。字典会检查有没有哪一对的键是“Anna”,如果有,返回它的值。如果字典没有找到键,返回 nil。

print(namesAndScores["Greg"])
// > nil

还记得数组如果下标越界访问就会导致一个运行时错误吗,但字典不同,因为他们的结果被包装为可选值。

你现在可能还没有理解,但可选值的下标访问真的强大。它允许你找一个特定选手是否在游戏里,而不需要遍历所有的键,但使用数组的话就必须要这么做了。

使用 properties 和方法

字典有相同的 isEmpty 和 count properties,和数组一样:

print(namesAndScores.isEmpty)
// > false
print(namesAndScores.count)
// > 4

如果你只想看字典的键或值,你可以从字典的 keys 或 values properties 创建一个数组,各自的:

print(Array(namesAndScores.keys))
// > ["Brian", "Anna", "Craig", "Donna"]
print(Array(namesAndScores.values))
// > [2, 2, 8, 6]

这些就是你在前一篇文章里学的常规数组。

修改字典

添加对

Bob 想加入游戏。

让他加入之前看看他的信息:

var bobData = ["name": "Bob", "profession": "Card Player", "country": "USA"]

这个字典是 [String: String] 类型的,它可以被修改因为被分配到变量了。假设你获得了更多关于 Bob 的信息,想把它加到字典里。这么做:

bobData.updateValue("CA", forKey: "state")

甚至还有一个更短的方式,使用下标:

bobData["city"] = "San Francisco"

Bob 是一个职业纸牌选手。到目前为止,他听起来很适合填入你的名单。

迷你练习

写一个函数,打印给定选手的城市和国家。

更新值

又出现了之前的情况,Bob 曾经在玩牌的时候被逮到作弊。他不只是专业的,还是出老千的!他让你改掉他的名字和职业,这样就没人会认出他了。

看起来 Bob 热切的希望浪子回头,你同意了。首先,你把他的名字从 Bob 改为了 Bobby:

bobData.updateValue("Bobby", forKey: "name")
// > Bob

你读添加对的时候就已经看过这个方法了。为什么它返回字符串“Bob”?updateValue(_:forKey:) 用新的值替换了给定键的值,然后返回老的值。如果键不存在,这个方法会添加新的一对,然后返回 nil。

就像添加一样,你可以用更少的代码来完成,使用下标:

bobData["profession"] = "Mailman"

就像第一个方法,代码更新了这个键的值,或者如果键不存在,创建新的一对。

移除对

Bob-额,抱歉-Bobby,还是觉得不够安全,他希望你移除所有有关他来自哪里的信息:

bobData.removeValueForKey("state")

这个方法会移除“state”键以及它在字典中的关联值。就像你可能预料到的,使用下标是更简短的方式:

bobData["city"] = nil

分配 nil 为一个键的关联值会从字典中移除这一对。

遍历字典

当你想遍历字典的时候,for-in 循环也是有效的。但因为字典里的项目都是成对的,你需要用元组:

for (key, value) in namesAndScores {
    print("\(key) - \(value)")
}
// > Brian - 2
// > Anna - 2
// > Craig - 8
// > Donna - 6

只遍历键也是可以的:

for key in namesAndScores.keys {
  print("\(key), ", terminator: "") // no newline
}
print("") // print one final newline
// > Brian, Anna, Craig, Donna,

你可以用同样的方式只遍历值,通过字典的 values property。

序列操作

你也可以对字典做序列操作。例如,你可以用 reduce(_:combine:) 来替换前一个代码片段为只一行代码:

let namesString = namesAndScores.reduce("", combine: { $0 + "\($1.0), " })
print(namesString)

在一个 reduce 语句里,$0 是指部分合并的结果,$1 指向当前元素。因为字典的元素是元祖,你需要使用 $1.0 来获得这一对的

让我们看看如何用 filter(_:) 来找到所有分数不足 5 的选手:

print(namesAndScores.filter({ $0.1 < 5 }))
// > [("Brian", 2), ("Anna", 2)]

这里用 $0.1 来访问这一对的

字典操作的运行时间

为了能够检测字典是如何运作的,需要理解哈希(hashing)是什么,以及它是如何工作的。哈希是转换值 - String, Int, Double, Bool, 等等 - 到数字化的值的过程,叫做哈希值

Swift 里,所有基本类型都是可以被哈希的,并且有一个 hashValue property。这是例子:

print("some string".hashValue)
// > 4799450059642629719
print(1.hashValue)
// > 1
print(false.hashValue)
// > 0

哈希值必须是确定性的 - 意味着给定一个值必须总是返回同样的哈希值。不论你计算“some string”的哈希值,总是会得到相同的值。

字典只能存储可以被哈希的键。这是一个超出本教程范围的细节实现了,但处理哈希让检查独一性和搜索更轻松了。

这是多种字典操作表现的消耗:

分配元素:获得指定键的值是一个常量时间操作,即O(1)。
加入元素:要插入一个元素,字典需要计算这个键的哈希值,然后基于那个哈希来存储数据。这些都是 O(1) 操作。
删除元素:再一次,字典需要计算哈希值来确切了解在哪里可以找到元素,然后移除它。这也是一个 O(1) 操作。
搜索元素:像上面提到的,访问元素有一个常量运行时间,所以搜索的复杂度也是 O(1)。

关键点

  • 字典是没有顺序的键值对集合。
  • 字典的都是相同类型的,值也是相同类型的。
  • 使用 下标 来获得值,以及添加、更新或移除对。
  • 遍历字典会返回元祖,同时包含键和值。

接下来去哪儿?

字典和数组相当不同:他们没有顺序,有键和值,以及有杰出的搜索表现。
但是,接口在下标和 for-in 循环上很相似。你会在后面学习到,“协议”,这个相似性要感谢跨越集合类型通用的接口。
在下一篇,你会学习集合(sets),结合了你已经从字典和数组里学到的概念。

挑战

挑战 A:你就是编译器

下列哪个是有效语句?

1. let dict1 = Int, Int()
2. let dict2 = []
3. let dict3 = [Int: Int]()

对于接下来四个语句,使用下面的字典:

let dict4 = ["One": 1, "Two": 2, "Three": 3]

4. dict4[1]
5. dict4["One"]
6. dict4["Zero"] = 0
7. dict4[0] = "Zero"

对于接下来的三个语句,使用下面的字典:

var dict5 = ["NY": "New York", "CA": "California"]

8. dict5["NY"]
9. dict5["WA"] = "Washington"
10. dict5["CA"] = nil

挑战 B:替换字典值

写一个函数,交换字典里两个键的值。这是函数结构:

func swapValueForKey(key1: String, withValueForKey key2: String, inDictionary: [String: Int]) -> [String: Int]

挑战 C:搜索字典

给定一个字典,以两个字母的州代号为键,并以完整的州名为值,写一个函数输出所有名字不超过八个字符的州。例如,对于字典 ["NY": "New York", "CA":"California"],输出应该是 “California”。

挑战 D:组合字典

写一个函数,把两个字典组合为一个。如果一个特定的键在两个字典都出现了,忽略第一个字典的对。这是函数结构:

func combine(dict1: [String: String], with dict2: [String: String]) ->[String: String]

挑战源代码

https://yunpan.cn/cMwZ6jA956jsC (提取码:0df7)


介绍

欢迎来到Swift世界!Swift是一门苹果在2014年夏天发布的编程语言。从那之后,Swift发布了一个主要的版本跳跃,成为了开始在苹果平台:iOS,OS X,watchOS和tvOS开发的最简单的方式。

谁适合这篇教程

这篇教程适合懂一点编程、并且希望学习Swift的人。也许你已经为网站写过一些JavaScript代码,或者用Python写过一些简短的程序。这篇教程就是为你准备的!你会学习到编程的基本概念,同时也会成为Swift语言小能手。

如果你是赤裸裸的编程新手,这篇教程也是为你准备的!教程里贯穿有简短的锻炼和挑战来给你一些编程练习,同时测试你的知识。

需要准备什么

要看这篇教程,你需要准备如下的东西:

  • 一台运行OS X El Captian(10.11)的Mac,带有最新发布的更新并且安装了安全补丁。这样你才能够安装需要的开发工具:最新版本的Xcode。
  • Xcode 7.3 或更新的版本。Xcode是用Swift写代码的主要开发工具。最小也需要Xcode 7.3版本,因为那个版本包含Swift 2.2。你可以免费从Mac App Store下载Xcode的最新版本,这里:http://apple.co/1FLn51R

如果你还没有安装Xcode最新版本,在继续看下面的教程前要确定安装。

如何使用这篇教程

每篇教程都会介绍触手可及的话题理论,伴随大量Swift代码来示范在学习的实际的应用程序。

教程里的所有代码都是平台中立的;这意味着不是为iOS、OS X或任何其它平台而特定。代码在playgrounds里运行,你在本篇中已经学习了。

在剩下的教程里,你可以把代码在自己的playground里输入进去。这样你就可以和代码“玩耍”(play around),做一些改变立即就能看见代码运行的结果。

剩下的教程里会贯穿实际小练习,都是简短的练习,关于触手可及的主题。每篇的末尾也有挑战,会有编程问题也会有长一点的代码练习来测试你的知识。做完就能掌握大部分的Swift基础知识。

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

推荐阅读更多精彩内容