集合类型
Swift提供了三种集合类型,数组、set和字典,用来存放一系列内容。数组顺序存放统一类型的值。set是互不相同值的无序集合。字典也是键值对的无序集合。
Swift中数组、set和字典的键和值的类型是明确的。这就意味着你不能将一个不符合的类型值塞入到集合类型中。也意味着从集合中取到的类型一定是确定的。
Note
Swift的数组、set和字典是作为泛型集合的实现的。更多的关于泛型类型和集合的内容,参见 泛型 一章。
集合的易变性
如果你创建了一个集合,并且将其赋值给了一个变量,这个集合就具有了易变性。意思就是在这个集合被创建后,它可以被添加内容、移除内容、或者改变内容。相反,如果你将这个集合赋值给了一个常量,它就不能被修改了,他的长度和内容都不能被修改。
不能被修改的集合最好声明为常量,这样Swfit编译器会对其优化处理。
数组
数组可以接受多个同一类型的值,同一值可以出现在多个不同的位置。
Swfit的数组和OC中的不同点在于其可以存放的值类型,OC中的NSArray 和NSMutableArray 能够接受任何类型的值而且在访问其中内容时没有任何有关类型的提示。Swift的数组存放类型是明确的,类型声明可以是一个明确的类型或者接口或者特定类。如果你创建了一个Int类型的数组,你就不能向其添加任何非Int的数据。Swift的数组是类型安全的,你能明确的知道其中存放的内容类型。
数组类型的简短语法
完整版:Array 或者简化版:[SomeType]。尽管二者功能效果一样,但简化版本是首选,本文将在涉及这方面的情形都采用简化版本。
数组字面值
你可以在用array声明一个数组后,给它赋值,赋值的格式如下:
[value 1,value 2,value 3]
下面的例子创建了一个叫shoppingList 的数组,里面存储字符串:
var shoppingList: [String] = [”Eggs”,”Milk”]
// shoppingList has been initialized with two initial items
这个例子中字面值包括了两个字符串没有其它类型,和数组的类型声明一致,所以赋值语句被允许进行,两个字符串作为初始化的参数给了初始化方法。
凭借Swift的类型接口,如果你的初始化数据里面是相同的类型,你可以省去数组的类型定义:
var shoppingList = [”Eggs”,”Milk”]
因为所有的值都是同一类型,所以Swift可以推断出变量shoppingList正确的数组类型。
访问和修改数组
你可以通过数组的方法和属性对其进行访问和修改,当然也可以通过下标。
通过只读属性count 获得数组内容的个数:
println("The shopping list contains \(shoppingList.count) items.") // prints "The shopping list contains 2 items."
通过布尔类型的属性isEmpty 判断数组是否有内容,这是判断count 等于0的一个简化方式:
if shoppingList.isEmpty { println("The shopping list is empty.") } else { println("The shopping list is not empty.") } // prints "The shopping list is not empty."
shoppingList.append("Flour") // shoppingList now contains 3 items,and someone is making pancakes
shoppingList += ["Baking Powder"] // shoppingList now contains 4 items shoppingList += ["Chocolate Spread","Cheese","Butter"] // shoppingList now contains 7 items
通过下标访问数组内容:
var firstItem = shoppingList[0] // firstItem is equal to "Eggs"
Swift的数组下标从0开始。
你也可以通过下标修改数组内容:
shoppingList[0] = "Six eggs" // the first item in the list is now equal to "Six eggs" rather than "Eggs"
你可以使用下标一次替换数组内一定范围的内容,如果你要修改的范围和你提供的修改值个数不符也没有关系。
shoppingList[4...6] = ["Bananas","Apples"] // shoppingList now contains 6 items
疑问:用下标的范围表示法?是采用0开始还是1?上例中操作前数组长度为6吧?
你不能通过下标向一个数组照插入内容。通过下标访问和修改内容时,如果下表超出范围,会有一个运行时错误。你可以通过检查数组长度(通过count 属性)来避免上述情况。除非count为0的情况,数组的长度都是count-1.
向一个数组中插入内容,采用insert(atIndex:)方法:
shoppingList.insert("Maple Syrup",atIndex: 0) // shoppingList now contains 7 items // "Maple Syrup" is now the first item in the list 将"Maple Syrup"添加到数组的第一个位置
删除数组中特定内容采用removeAtIndex ,这个方法还会返回删除的内容,当然你可以完全忽略这个返回值。
let mapleSyrup = shoppingList.removeAtIndex(0) // the item that was at index 0 has just been removed // shoppingList now contains 6 items,and no Maple Syrup // the mapleSyrup constant is now equal to the removed "Maple Syrup" string
删除操作后,数组中出现了间隙,后续的内容会向前填充。这时的第一个元素已经是:”Six eggs”
firstItem = shoppingList[0] // firstItem is now equal to "Six eggs"
如果你要删除数组的最后一个元素,那么可以采用removeLast ,这样就可以避免使用removeAtIndex 删除时还得调用count 。和removeAtIndex 一样,removeLast 范围你删除的内容。
let apples = shoppingList.removeLast() // the last item in the array has just been removed // shoppingList now contains 5 items,and no apples // the apples constant is now equal to the removed "Apples" string
遍历数组
可以采用for-in遍历数组
for item in shoppingList { println(item) } // Six eggs // Milk // Flour // Baking Powder // Bananas
如果你需要根据数组的索引做些事情,此时可以使用全局方法enumerate遍历数组。enumerate 返回数组对应的元组,其中包括了内容的索引和内容值。处理元组就可以解析数组内容了。
for (index,value) in enumerate(shoppingList) { println("Item \(index + 1): \(value)") } // Item 1: Six eggs // Item 2: Milk // Item 3: Flour // Item 4: Baking Powder // Item 5: Bananas
具体可以查看循环 for-in语法。
创建和初始化数组(本节已经删除)
你可以创建一个特定类型的空数组:
var someInts = [Int]() println("someInts is of type [Int] with \(someInts.count) items.") // prints "someInts is of type [Int] with 0 items."
这样写,someInts只接受Int类型的数据。
在给定了数组的类型后,可以使用“[]”初始化或者复制
someInts.append(3) // someInts now contains 1 value of type Int someInts = [] // someInts is now an empty array,but is still of type [Int]
Swift的数组初始化方法提供了设置数组长度和缺省值的功能。数组长度对应的参数叫:count,缺省值对应的参数叫:repeatedValue
var threeDoubles = [Double](count: 3,repeatedValue: 0.0) // threeDoubles is of type [Double],and equals [0.0,0.0,0.0]
创建了一个类型为Double,长度为3,缺省值为0.0的数组。
你可以用+将两个数组组合在一起,创建出了一个类型相同的数组:
var anotherThreeDoubles = [Double](count: 3,repeatedValue: 2.5) // anotherThreeDoubles is inferred as [Double],and equals [2.5,2.5,2.5] var sixDoubles = threeDoubles + anotherThreeDoubles // sixDoubles is inferred as [Double],2.5]
Sets
一个Set存储相同类型的不同值,而且不排序。可以在不关心排序的情形使用Set替代数组。在需要限制一个元素在集合中只能出现一次时,使用Set。
NOTE
Swift的Set类型是与系统底层NSSet类的桥梁。更多的关于使用Set操控底层和Cocoa的信息,参见 用Swift操控Cocoa和Objective-C。
字典
字典是个存放同一类型内容的容器,在字典中每个内容都有唯一对应的键与其对应。和数组不同,字典中的内容没有特定的顺序而言。和现实中的字典一样,你可以通过键查找字典中的内容。
Swift的字典明确了键和值的类型。这和OC中的NSDictionary 、NSMutableDictionary 不同。
字典类型的简写
Dictionary
字典的字面值
你可以像数组那样给出字典的字面值用于初始化。字面值是若干键-值对构成的。
[key 1: value 1,key 2: value 2,key 3: value 3]
下面这个例子中,字典的键是三个字母构成的航空协会编码,对应的值是机场的名字:
var airports: [String: String] = [”YYZ”: ”Toronto Pearson”,”DUB”: ”Dublin”]
这个名字为airports的字典是一个[String:String]类型,意思就是一个键是String类型、值是String类型的字典。
这个字典被定义为变量,因为我们后面需要修改它。
和数组类似,省去类型后,简写的方式为:
var airports = [”YYZ”: ”Toronto Pearson”,”DUB”: ”Dublin”]
因为赋值的内容 类型都一样,所以Swift编译器可以推断出他们的正确类型。
访问和修改字典
访问和修改字典可以通过它的属性和方法,或者通过下标。和数组一样,count这个只读属性可以告诉你字典的长度。
println("The airports dictionary contains \(airports.count) items.") // prints "The airports dictionary contains 2 items."
isEmpty 属性判断一个字典是否长度是0
if airports.isEmpty { println("The airports dictionary is empty.") } else { println("The airports dictionary is not empty.") } // prints "The airports dictionary is not empty."
可以采用下标添加一个内容到一个字典,使用一个从未使用的键做下标,然后赋值给它(键值的类型都要对):
airports["LHR"] = "London" // the airports dictionary now contains 3 items
airports["LHR"] = "London Heathrow" // the value for "LHR" has been changed to "London Heathrow"
另外的一种方式通过updateValue(forKey:)方法,设置或修改指定键的内容。和下标方式一样,这个方式在没有对应的值的时候创建一个值给对应的键,在有对应值的时候修改那个值。和下标方式不同的地方是,这个方法在修改了值的时候,会返回原来的值。这个功能让你能够检查更新有没有生效。
updateValue(forKey:)返回的是一个字典值类型的可选类型。如果做了修改操作,将返回修改前的值;否则将返回nil;
if let oldValue = airports.updateValue("Dublin Airport",forKey: "DUB") { println("The old value for DUB was \(oldValue).") } // prints "The old value for DUB was Dublin."
同理,用下标访问字典的内容,返回的也是一个字典值类型的可选类型。有值返回值,没有值返回nil。
if let airportName = airports["DUB"] { println("The name of the airport is \(airportName).") } else { println("That airport is not in the airports dictionary.") } // prints "The name of the airport is Dublin Airport."
通过下标方式,用nil赋值,可以删除字典中的一个键值对
airports["APL"] = "Apple International" // "Apple International" is not the real airport for APL,so delete it airports["APL"] = nil // APL has now been removed from the dictionary
removeValueForKey 是另外一种删除字典键值对的方式,这个方法在返回删除掉的值或者nil。
if let removedValue = airports.removeValueForKey("DUB") { println("The removed airport's name is \(removedValue).") } else { println("The airports dictionary does not contain a value for DUB.") } // prints "The removed airport's name is Dublin Airport."
遍历字典
你可以通过for-in语句遍历一个字典,每个字典有对应有一个由键值对构成的元组,处理这个元组即可。
for (airportCode,airportName) in airports { println("\(airportCode): \(airportName)") } // LHR: London Heathrow // YYZ: Toronto Pearson
当然你可以通过遍历键或者值的方式遍历整个字典:
for airportCode in airports.keys { println("Airport code: \(airportCode)") } // Airport code: LHR // Airport code: YYZ for airportName in airports.values { println("Airport name: \(airportName)") } // Airport name: London Heathrow // Airport name: Toronto Pearson
如果你想将字典的键或者值分别存放到数组中,创建两个数组就行了:
let airportCodes = [String](airports.keys) // airportCodes is ["LHR","YYZ"] let airportNames = [String](airports.values) // airportNames is ["London Heathrow","Toronto Pearson"]
字典是无序的容器,The order in which keys,values,and key-value pairs are retrieved when iterating over a dictionary is not specified.
创建一个空的字典
var namesOfIntegers = [Int: String]() // namesOfIntegers is an empty [Int: String] dictionary
如果字典的类型确定了,可以用[:]将字典重新置为空:
namesOfIntegers[16] = "sixteen" // namesOfIntegers now contains 1 key-value pair namesOfIntegers = [:] // namesOfIntegers is once again an empty dictionary of type [Int: String]
字典键类型要有哈希值
字典键的类型需要提供比较哈西值的方法。哈西值是一个Int的数字,用来比较两个对象是否相等的。这么说吧,如果两个对象相等,那么他们的哈希值相等。
所有Swfit的基本类型都有默认的哈希值,自然可以作为字典的键。没有关联值(在枚举中有描述)的枚举成员值默认都是hashable的。
NOTE
可以用自定义的类型做字典的键,前提是你的类遵循Swift的标准库中的Hashable 协议。具体做法是需要有个一叫做hashValue的Int类型的可读属性(getter),还要给出“相等”操作符(==)的实现。一个类型的hasValue属性返回值不需要经过相同程序的执行得到一样的结果,同样不需要经过不同的程序执行得到一样的结果。