https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-ID105
Swift提供3种集合类型, 包含数组Array、集合Set和字典Dictionary, 功能类似于Java的ArrayList、HashSet和HashMap。Array数组是地址连续且有序的值, Set是地址不连续且存储的值唯一,字典Dictionary存储了一些key-value。 PS:有Java基础的很好理解。
Array、Set和Dictionary存储的数据必须是约定的数据类型, 不能插入其它数据类型, 所以从集合中取出的数据肯定是同一个数据类型。
集合的可修改性:
实例化一个Array、Set或者Dictionary后, 你可以增删改查其中的数据(使用var关键字)。 但是当你将集合声明为常量时(即使用let关键字), 集合的数据不可修改。
数组Array:
在连续地址空间存储类型相同的值,同一个值可以出现多次。Swift的Array可直接操作Object-C的NSString对象。 通过2种方式创建Array, 即 Array<T> 或者 [T] 。 T是数组元素的类型, 在这里是泛型。(跟Java的ArrayList一样使用泛型)
创建空数组:
var someInts = [Int]() //somInts是Int型数组
print("someInts is of type [Int] with \(someInts.count) items.")
// 输出"someInts is of type [Int] with 0 items."
<pre name="code" class="java"> someInts.append(3) //在数组末尾插入数字3, 看着像Java的StringBuilder/StringBuffer用法
// someInts数组含有1个数据3
someInts = [] // someInts 变为空数组, 仍然是整型数组[Int]。 类似于Java的clear方法
使用默认值创建数组:
Swift支持创建具有默认值的数组, 即在实例化数组时对其中的元素赋值。 第一个参数repeating是参数值(根据值确定其数据类型), 第二个参数是数组个数。
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles是个[Double]数组, 包含3个相同值即[0.0, 0.0, 0.0]
功能类似于C++语言的
double[] threeDouble = new double[3];
memset(threeDouble, 0, sizeof(threeDouble));
数组拼接(类似于String的拼接)
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles是[Double]数组, 值是 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles也是[Double]数组, 值等于2个数组的并集即 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
使用数组标识符[]创建数组(类似于使用Java的{}创建数组)
var shoppingList: [String] = ["Eggs", "Milk"] //shoppingList是String数组,包含Eggs和Milk
<code class="code-voice"><span class="kt">var</span> <span class="vc">shoppingList</span> = [<span class="s">"Eggs"</span>, <span class="s">"Milk"</span>]</code> //Swift语法中可以省略类型参数,而且跟上面语句的功能完全一致! <strong><span style="color:#FF0000;">前提是数组内容的数据类型必须都相同</span></strong>
等同于Java的
String[] shoppingList = {"Eggs", "Milk"};
读取和修改Array数据:
print("The shopping list contains \(shoppingList.count) items.")
// 输出"The shopping list contains 2 items.", 使用成员变量count得到数组元素个数
<pre name="code" class="java">if shoppingList.isEmpty { //使用isEmpty属性判断数组是否为空, 即包含0个元素
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.") //因为数组里有2个元素、非空,所以isEmpty等于false
}
// Prints "The shopping list is not empty."
shoppingList.append("Flour") //使用append方法向数组里添加元素
shoppingList += ["Baking Powder"] //前面已经提到了,2个数组之间用+号分隔表示数组拼接。
// shoppingList包含4条数据
shoppingList += ["Chocolate Spread", "Cheese", "Butter"] //再添加3条后,count等于7
<pre name="code" class="java">var firstItem = shoppingList[0] //数组是连续地址空间,可以通过下标得到对应数据,下标范围[0,count),
跟其它语言一样。
<pre name="code" class="java">shoppingList[4...6] = ["Bananas", "Apples"] //批量替换数组中的元素4...6表示4、5和6,即3个元素。
等同于
shoppingList[4] = "Bannanas"
shoppingList[5] = "Apples"
因为左边4/5/6等3个下标需要修改值, 但右侧只有2个值Bannans/Apples, 下标6缺少对应的值;在Swift语言中遇到
这种情况时会删除下标6的元素, 即shoppingList数组长度减1, 从7变为6。
PS:这个语法在内存优化方面很有用, 一句话就删除了数组冗余的空间。 想想Java是怎么做的? 新创建个小数组,
将大数组中有用的值拷进来, 然后删除大数组的引用。
如果想在数组某个位置插入一条数据时, Swift提供了insert(_:at:)
方法。 比如:
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList数组在下标0即首部插入一条数据, 共包含7条数据。 而且"Maple Syrup"是下标0即第一条数据
PS:你只要知道insert方法, XCode会提示用法的。
那如何删除数组元素呢? Swift提供了remove方法, 我觉得猜也能猜到 无非是remove或者delete。
let mapleSyrup = shoppingList.remove(at: 0) //同insert用法, 删除指定下标的数据, 数组长度减1
//“Maple Syrup”这条数据被删除了, 数组长度变为6
遍历数组(Swift提供了跟Java类似的for-in语法)
for item in shoppingList { //item是String类型
print(item)
}
或者通过下标做循环
var i = 0
while i < shoppingList.count {
print(shoppingList[i])
i += 1
}
对于Array中的每个元素, enumerated()
方法可以返回其对应的键值对(index, value),即(下标, 值)。
for (index, value) in shoppingList.enumerated() { //index是Int型下标,value跟数组元素数据类型相同
print("Item \(index + 1): \(value)")
}
输出:
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
小结: Swift的Array数组跟Java的类似, 但新增了几个语法。 比如批量替换数据很牛X, 因为它还能缩短数组长度!!! 要知道Java为了避免数组空间浪费, 还弄了个稀疏矩阵SparseArray解决这个问题。
Set语法(跟Java类似,很好理解。 跟Array的区别就是Set里的数据都是唯一的、跟其它数据不一样)
var letters = Set<Character>() //声明一个字符型集合, 是空的哦!
print("letters is of type Set<Character> with \(letters.count) items.")
// 输出"letters is of type Set<Character> with 0 items."
Set类型也可以像Array那样插值和置空
letters.insert("a") //插入第一个数据, 注意用的是双引号!
letters = [] //letters变为空, 数据类型不变, 仍然是Set<Character>
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres集合包含3条记录, 记录都是String类型
回想上面数组的定义方式, Swift语言可以通过记录类型确定集合的类型, 所以还可以这样写
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"] //Set后面没有数据类型, 但它仍然等同于Set<String>
跟Array一样的判空方式, 如:
if favoriteGenres.isEmpty { //favoriteGenres包含3个记录, isEmpty为false
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// 输出"I have particular music preferences."
favoriteGenres.insert("Jazz") //非常熟悉的insert方法,即插入数据
if let removedGenre = favoriteGenres.remove("Rock") { //注意这里的语法,removedGenre常量只是为了下面打印语句使用, 也可以写成 if favoriteGenres.remove("Rock") { 。 如果值为nil则进入else分支, 值不是nil则进入if分支。
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// 输出 "Rock? I'm over it."
如果想判断Set里是否包含某元素,如何做? 第一反应肯定是contains方法, Swift也是这么干的, 这点跟Java相同。
if favoriteGenres.contains("Funk") { //判断favoriteGenres里是否含有Funk, 有则为true,无则为false。
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// 输出"It's too funky in here."
Set跟Array一样使用for-in循环遍历:
for genre in favoriteGenres {
print("\(genre)")
}
Set还提供了4个集合方法:假设a和b都是Set集合。
intersection(交集): a.intersection(b) 会得到二者交集;
symmetricDifference(相加并删除相同部分): a.symmetricDifference(b)会得到二者相加且删掉相同部分后的集合。
union(并集): a.union(b)会得到二者的全部;
subtracting(删掉相同部分): a.subtracting(b)会得到删除与b相同部分后的a。
Dictionary字典:
Dictionary可以调用Object-C的NSDictionary, 包含的数据是key-value键值对, 跟HashMap功能一样。 key的数据类型都一样, value的数据类型都一样。
var namesOfIntegers = [Int: String]() //创建一个空Dictionary, 语法有点像Array。 key是Int型, value是String型。
namesOfIntegers[16] = "sixteen"
// namesOfIntegers有1个键值对了, 即(16, "sixteen")
namesOfIntegers = [:] //空Dictionary, 数据类型不变; 想想清空Array怎么做的? 是[] , 区分是有无冒号。
// namesOfIntegers数据类型不变,仍然是Dictionary的 [Int: String]、
跟初始化Array类型, 初始化Dictionary就是多了冒号。
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//airports是Dictionary<String,String>类型, 包含2个键值对
就像Set一样, 在声明Dictionary时不必写数据类型, Swift会根据值判断数据类型。
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] //省略了[String: String]但功能一样
- airports["LHR"] = "London" //airports里key没有"LHR", 在这里会新插入一条数据, airports包含3条键值对
- airports["LHR"] = "BeiJing" //airports里key存在"LHR", 在这里会更新对应的value。 airports仍然是3条键值对
- 说明:对于Dictionary, =可能是插入或者更新功能, 取决于Dictionary里是否存在相同key。
Swift还提供了updateValue(_:forKey:) 方法用于插入或者更新键值对, 跟=功能类似, 区别是updateValue会返回当前key对应的value, 如果Dictionary没有对应的key则是新增、跟=功能完全一样; 如果存在相同的key, 那么就是更新其键值并返回当前旧值, 这是=号做不到的。
所以可以通过判断updateValue返回值是否nil, 判断是插入或者更新。
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// 因为Dictionary里存在"Dub“的key, 所以是更新操作,函数返回“Dublin“,不是nil。 if为true, 所以输出"The old value for DUB was Dublin."
Dictionary跟Java的HashMap一样,也是通过key寻址删除键值对。 Swift提供了removeValue(forKey:_)函数, 找到键值对后会返回value并删除该键值对, 否则返回nil。
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// 输出"The removed airport's name is Dublin Airport."
遍历, 语法跟Array一样的for-in
遍历键值对:
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
遍历键:
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
遍历值:
-
for airportName in airports.values { print("Airport name: \(airportName)") }
- 说明: Java的HashMap只能遍历key, 因为有个缓存key的Set; 但没提供遍历value的方法; Dictionary能遍历键值对、键、值, API比Java更强大。
Dictionary还能拿到键、值的数组, 语法如下:
let airportCodes = [String](airports.keys)
// airportCodes 是["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
小结: Dictionary比Java的HashMap功能更强大, 尤其是在遍历功能上做的很完善。