一个集合也是能够存放多个相同类型元素的收集。不过它与数组不同的是:
一个集合中不允许出现两个完全相同的元素。
一个集合中的数据元素是无序的。
并不是所有类型的对象都能作为集合的元素,只有遵循 Hashable 协议类型的对象才能作为集合的元素。
不过所幸的是,Swift中的所有基本类型(包括:String、Int、Float、Double、Bool)都遵循了 Hashable 协议,所以这些基本类型的对象都能作为集合的元素。
集合的完整类型为,Set<Element: Hashable>,这里的 Element 表示集合元素的类型。集合类型没有精简表示法。
我们可以使用集合的构造方法创建一个空集合,也可以使用数组字面量来构造一个集合。各位在使用数组字面量时必须注意,需要给集合对象通过类型标注的方式显式添加集合类型,否则类型将被推导为数组类型。我们先看以下例子:
// 我们通过集合的构造方法来构造一个空的集合变量setA,
// setA集合对象的每个元素都是Int类型对象
var setA = Set<Int>()
// 我们这里还可以通过集合的不定参数个数的构造方法来创建一个含有指定元素的集合
setA = Set<Int>(arrayLiteral: 1, 2, 3, 4)
// 这里我们通过数组字面量来创建一个集合常量setB,
// 这里需要对setB显式指定类型
let setB: Set<Float> = [1.0, 0.5, 2.0]
// 我们也可以用空的数组字面量来构造一个空的集合
setA = []
集合的元素访问
- 集合对元素的存放本身就不是基于索引,而是基于元素对象的哈希值。
所以我们基本通过两种途径对集合元素的值进行访问:第一种是通过 for-in 循环,通过迭代的方式将集合中的元素一一取出;第二种是通过集合对象的 flatMap 实例方法,有选择性地将集合中的元素取出,然后放到一个数组里返回。
let s: Set<Int> = [1, 2, 3]
// 查询集合常量s的元素个数
print("count = \(s.count)")
// 查询集合常量s是否为空集合
print("Is empty? \(s.isEmpty)")
// 判定集合常量s是否含有3
print("contains 3? \(s.contains(3))")
var a: Set<Int> = [4, 5, 6]
// 对变量集合a插入元素7
a.insert(7)
// 我们先打印当前集合变量a的元素,
// 这里打印出的元素序列是:[5, 6, 7, 4]
print("a = \(a)")
// 然后我们再插入元素7
a.insert(7)
// 再打印集合a的元素,
// 仍然为:[5, 6, 7, 4]
print("a = \(a)")
// 对集合变量a删除一个元素6
a.remove(6)
// 此时a的元素序列为:[5, 7, 4]
print("a = \(a)")
其实Swift中的集合与离散数学上的集合概念非常贴近,我们可以直接用 == 操作符来比较两个集合对象是否相同。如果两个集合对象完全相同,那么它们必须满足三个条件:自反性、对称性以及传递性。此外在Swift中,我们可以对一个变量集合做求交集、并集以及差集。
如何让自定义类型的对象能作为集合的元素 86page
let s1: Set<Int> = [1, 2, 3]
let s2: Set<Int> = [3, 2, 1]
// s1与s2这两个集合完全相同
if s1 == s2 {
print("s1 == s2")
}
/// 这里自定义了一个MyStruct结构体,
/// 并遵循了Hashable协议
struct MyStruct: Hashable {
/// 定义实例属性a
var a = 0
/// 定义实例属性b
var b = 0.0
// 这里是实现Hashable中的实例计算属性hashValue
// 这里的实现比较简单,而且也并不能保证哈希值的全局唯一性,
// 仅仅作为demo使用
public var hashValue: Int {
return a.hashValue ^ b.hashValue
}
// 这里实现了Equatable协议中的 == 操作符类型方法
public static func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b
}
}
var set = Set<MyStruct>()
// 我们插入两个MyStruct对象
set.insert(MyStruct(a: 10, b: 1.0))
set.insert(MyStruct(a: -10, b: 0.5))
// 打印set的元素
print("set = \(set)")
字典
字典与数组和集合有所不同,它是以一种键-值对的方式存储元素的。这就跟我们的汉语字典非常类似,比如我们通过拼音查字,拼音就是“键”,然后由它根据指定的页码找到的汉字就是“值”。只不过Swift中字典的键所对应的值必须是唯一的,也就是说字典中不允许出现两个相同的键,但不同的键可对应相同的值。而字典与集合类似的是,它也属于无序收集类型。
Swift编程语言中,字典中的键的类型与值的类型都是在声明中直接确定的,每个键的类型都应该是同一种,每个值的类型也必须是同一种,当然键与值的类型可以相同也可以不同。此外,键的类型与集合元素一样,必须是遵循 Hashable 协议的类型。而值的类型则与数组一样,可以是任意类型。
字典的完整类型为:Dictionary<Key: Hashable, Value>。这里的 Key 表示键的类型;Value 则表示值的类型。字典类型也有精简表示法:[Key : Value]。我们可以使用字典类型的构造方法创建一个字典对象实例,如以下代码所示:
// 使用字典默认的构造方法创建一个空的字典变量。
// 该字典的键是String类型,值是Int类型
var a = Dictionary<String, Int>()
// 通过字典的不定参数个数的构造方法创建一个字典对象,
// 这里通过元组将字典的键值对进行捆绑,
// 元组的第一个元素是键,第二个元素是值。
a = Dictionary<String, Int>(dictionaryLiteral: ("one", 1), ("two", 2), ("three", 3))
// 这里也是使用字典的默认构造方法创建一个空的字典常量。
// 该字典的键是Int类型,值为Float类型
let b = [Int : Float]()
// 这里也是通过字典的不定参数个数的构造方法创建一个字典常量
let c = [Int : Float](dictionaryLiteral: (0, 0.0), (1, 1.1), (2, 2.2))
我们也可以使用字典字面量来创建一个字典对象实例,字典的字面量的形式为: [key1 : value1, key2 : value2, ..., keyn, valuen]。我们看以下代码例子:
// 这里通过一个空字典字面量创建一个空字典。
// 这里必须显式声明字典变量a的类型
var a: [String : Int] = [:]
// 这里的字典字面量构造了一个含有3个键值对的字典对象实例
a = ["one":1, "two":2, "three":3]
对字典的值的访问类似于数组对元素的访问。Swift中可以通过下标操作符对字典的值进行访问,比如对于一个 [String : Int] 的字典对象 dict ,我们可以通过 dict["string"] 的方式来访问键 "string" 所对应的值,也就是说这里下标操作符中的表达式类型必须与键的类型相兼容。不过这里所得到的值是一个Optional对象,因为如果指定的键不存在对应的值的话,那么结果会返回空。当然,我们也可以使用 updateValue(_:forKey:) 方法来更新一个字典变量指定健的值,不过这显然没有直接使用下标的方式简洁。我们看以下代码例子:
// 这里声明了一个[String:Int]类型的字典常量dict,
// 它具有3个键值对元素
let dict = ["one":1, "two":2, "three":3]
// 访问键"one"对应的值,可得到1
let one = dict["one"]
// 访问键"two"对应的值,可得到2
let two = dict["two"]
// 访问键"three"对应的值,可得到3
let three = dict["three"]
// 访问键"four"对应的值
// 由于这里没有键"four",所以将得到空值
let four = dict["four"]
// 这里的one、two、three、four常量都是optional对象
print("one = \(String(describing: one)), two = \(String(describing: two)), three = \(String(describing: three)), four = \(String(describing: four))")
下面介绍字典常用的基本实例方法与属性。与数组和集合一样,字典对象实例可访问 count 属性来获取当前字典对象的键值对元素个数;通过访问 isEmpty 属性来获取当前字典是否为空,即不包含任何元素;通过访问 keys 属性来获取当前字典对象的所有键;通过访问 values 属性来获取当前字典对象的所有值。这里各位要注意的是, keys 属性的类型的是遵循 Collection 与 Equatable 协议的结构体类型;而 values 属性的类型则是遵循 MutableCollection 协议的结构体类型,它们与集合类型类似,不能通过下标方式访问其元素,而一般只能通过 for-in 循环迭代的方式逐一访问。
此外,对于变量字典还能通过调用下标方式来新增一个键值对或修改一个键对应的值;也可以通过 updateValue(_:forKey:) 实例方法来显式更新一个键对应的值。我们可以通过 removeValue(forKey:) 实例方法将指定键的元素给移除。我们下面来看一些简单例子:
// 这里声明了一个[String:Int]类型的字典常量dict,
// 它具有3个键值对元素
let dict = ["one":1, "two":2, "three":3]
// 我们查询字典常量dict中含有多少个元素
print("count is: \(dict.count)")
// 我们查询dict是否为一个空字典
print("Is empty? \(dict.isEmpty)")
// 我们打印出字典常量dict中所有的键
print(dict.keys, separator: ".", terminator: "v")
for key in dict.keys {
print("\(key)", separator: ",", terminator: " ")
}
// 我们打印出字典常量dict中所有的值
print(dict.values, separator: ".", terminator: "v")
for value in dict.values {
print("\(value)", separator: ",", terminator: " ")
// 我们创建了一个[Int:Double]类型的字典变量vdict
var vdict = [1:1.0, 2:2.0, 3:3.0]
// 我们这里新增一个键值对,
// 键为4,值为4.0
vdict[4] = 4.0
// 我们通过下标方式将键2对应的值修改为2.2
vdict[2] = 2.2
// 我们这里通过updateValue(_:forKey)方法将键3所对应值修改为3.3。
// 如果键3不存在,那么该方法将返回空;
// 否则返回键3原来对应的值
let old = vdict.updateValue(3.3, forKey: 3)
// 这里将键1对应的元素给移除
vdict.removeValue(forKey: 1)
print("old = \(old)")
print("vdict = \(vdict)")
字典字面量本身也是一个常量,如果我们对字典字面量直接调用updateValue(_:forKey:) 等具有元素修改的实例方法,则会引发编译器报错.
网友评论