1. Swift字典简介:
1) 用于存储键-值类型数据,和C++的map相似,其中键值不能重复,主要用于临时存放数据库中存取的记录,使用起来非常方便;
2) 字典类型名为Dictionary,和数组一样同样是类型固定的一种数据结构,就是指key-value的类型在初始化时通过类型推断或直接指定类型的方式就确定了,之后不得修改;
3) 定义初始化和数组类似:
键值对之间用,分隔,键-值之间用:分隔,外面用[]括起来
// 使用泛型定义
var person1: Dictionary<String, String> = ["name": "bill", "school": "lanxiang"]
println(person1) // [school: lanxiang, name: bill]
// 使用类型名[Type: Type]定义
var person2: [String: String] = ["name": "peter", "school": "xindongfang"]
println(person2) // [school: xindongfang, name: peter]
// 类型推导出是[Int: Double]类型的
var test = [12: 32.1, 34: 393.1, 3: 22.9]
println(test) // [12: 32.1, 3: 22.9, 34: 393.1]
和数组一样,如果使用类型推导定义时各个键值对类型不一致,内部就会用NSMetableDictionary替换!
同样,使用let定义的常量字典,其本身以及元素值都不可改变!
4) Dictionary实质上使用Swift的结构体实现的;
5) 创建空的字典:同样由三种方法,泛型构造器、类型构造器、直接置空(前提是类型得确定)
// 泛型构造器
var dict1 = Dictionary<Int, String>()
// 类型构造器
var dict2 = [Int: String]()
// 直接置空,但是必须得显示声明类型
var dict3: [Int: String] = [:] // 必须是空的键值对,所以:不能忘了!
// 如果不指定类型直接置空则会由于无法推导类型而使用NSMutableDictionary来实现
var dict4 = [:]
2. 字典的访问和修改:
1) 访问和直接改值和C++的map的做法一样:
var dic = [1: 2, 3: 5]
dic[10] = 17 // 对于不在字典中的键值直接添加
dic[1] = 5 // 对于存在于字典中的键值直接修改
println(dic[10]) // Optional(17)
println(dic[1]) // Optional(5)
2) 但是我们看到它输出的是可选类型的值,这里我们就要提一下Swift的Dictionary的安全机制了,对于不存在的键值对,如果访问了则只能返回一个nil,如果存在则返回相应的值,那么问题就来了,如果对于一个[Int: Int]类型的字典,如果一个不存在的键值dic[5]被直接使用了该如何呢,比如dic[5] + 2,但是dic[5]这对键值是不存在的!
其实Swift为了避免这个问题,将Dictionary的键值中的值的类型都会自动隐式地提升成可选类型,即虽然定义的时候是[Type: Type],但是底层是[Type: Type?],因此在使用字典中的值的时候一定要加上拆包符号才行,比如dic[5]! + 2,否则就会报错,这就是为什么不存在的键值访问时返回nil了:
var dic: [Int: Int] = [:]
println(dic[3]) // nil
dic[3] = 10
println(dic[3]) // 10
dic[3] + 7 // Bad! 没有拆包
println(dic[3]! + 7) // 17
3) 删除键值:两种方式,一种是直接赋nil,另一种是使用.removeValueForKey(forKey:)方法将制定键对应的值删除同时返回旧的值(如果旧的值本身就不存在就返回一个nil,因此该函数返回的是可选类型):
var dic: [Int: Int] = [1: 3, 10: 59]
println(dic.count) // 2
dic[1] = nil
println(dic[1]) // nil
println(dic.count) // 1
if let oldValue = dic.removeValueForKey(10)
{
println(dic[10]) // nil
println(oldValue) // 59
}
println(dic.count) // 0
!以上引出了一个新的常用技巧,就是if let的用法,专门用于快速判空,和C语言的if (!flag)相类似,这样可以避免使用if funcReturnOptional(...) != nil这样的麻烦形式,这在Swift中非常常用!并且if let也只能用在判空的情形,因为Swift中的空nil和true/false完全没有关系,因此不能用!flag这样的布尔运算;
!由于进了if let体中就意味着let定义的那个常量是必定有值的,因此Swift就将该常量设为确定类型的,因此在if let体中使用该常量时不得用!拆包而就当正常类型使用,如果多此一举使用!拆包会报错!
4) 修改键值:也是两种方式,一种是直接赋值(如果存在则修改,如果不存在则添加),另一种是.updateValue(newValue, forKey:)的方式,修改指定键对应的值同时返回旧的值,如果旧值不存在则返回nil,同样也是返回可选类型:
var dic: [Int: String] = [1: "haha", 2: "hihi"]
println(dic[2]) // hihi
dic[2] = "hoho"
println(dic[2]) // hoho
if let oldValue = dic.updateValue("hehe", forKey: 1)
{
println("旧值为:\(oldValue)") // 在if let中oldValue必定有值,所以是确定类型的而不是可选类型的,加!会报错
println("新值为:\(dic[1]!)") // dic[1]依然时可选类型的,需要加!,否则输出的就含Optional()
}
3. 字典的遍历:一共有三种方式,.keys域获得键集合,.values域获得值集合,元组分解遍历整个字典集合
// 键集合
for key in person.keys
{
println("ID: \(key)")
} // 2、3、1,可见顺序并不是初始化插入的顺序,内部使用非线性结构实现(红黑树)
// 值集合
for value in person.values
{
println("Name: \(value)")
}
// 使用元组分解整个字典集合
for (key, value) in person
{
println("ID: \(key), Name: \(value)")
}
对于类型非同一的Dictionary的遍历,要先使用as语句将底层的NSMutableDictionary转化成Dictionary的形式再遍历:
var dic = [1: 20.3, "haha": 32, 77.4: "good!"]
for key in (dic as Dictionary).keys
{
println(key)
}
for value in (dic as Dictionary).values
{
println(value)
}
for (key, value) in (dic as Dictionary)
{
println("key: \(key), value = \(value)")
}
4. 将keys和values转换为数组:直接使用强制类型转换即可
var dic: [Int: Double] = [1: 232.23, 2: 948.22, 3: 2948.2]
let keys = Array(dic.keys)
println(keys)
let values = Array(dic.values)
println(values)
5. 集合的复制:
1) 主要用于函数传参,如果函数的参数是集合,则会发生复制;
2) 由于Swift集合是值类型的,因此会复制其中的元素,但是如果集合中的元素包含类对象则需要特殊对待了!
3) 对于值类型的元素直接复制,对于类对象的元素只是复制引用(就是个指针),因此原本和副本的该字段的引用指向同一个类实体,因此如果在副本中通过该引用修改对象的字段,则也会影响到原本;
4) 显式的赋值直接使用赋值符号=就可以了;
6. 将字典的value值的类型进行转换:
1) 有时需要将字典的值进行转换,比如将String转换成字面所表示的Int值等;
2) Swfit的转换规则:对于Swift的基本类型之间的转换使用成员函数toType()即可,但是对于OC或者Cocoa Library中的类型之间的转换或者转到Swift的基本类型需要使用as语句,比如之前讲过的,对于底层是NSMutableDctionary的Swift的Dictionary要使用as将其转换成真正意义上的Swift的Dictionary才行:
var dic = [1: "20", 2: "abc"]
var val = dic[1]?.toInt() // value是可选类型的,可选类型调用其成员函数需要使用后缀?
println(val!) // 20
var dic = ["key1": "200", 3: 30] // 底层不是Swift的基本类型,而是NSObject,转到Swift基本类型需要用as
var val1 = dic[3] as Int // 此时的dic[3]是NSObject类型的,需要用as转,转成后就是正常的确定类型了
println(val1) // 30
// 此时的dic["key1"]也是NSObject类型的
// 同样也是先用as将其转换为正常的确定类型的String,再用toInt成员函数转换成可选类型的Int
var val2 = (dic["key1"] as String).toInt()
println(val2!)
// 也可以使用as?将OC或Cocoa类型转换为Swift的可选类型
var val3 = dic[3] as? Int // 转化成了Int?类型
println(val3!)
var val4 = (dic["key1"] as? String)?.toInt() // 先转成String?类型了
println(val4!)