Collection Types
目录[隐藏] |
集合类型
Swfit提供了两种集合类型:数组和字典序集合,用来存储一系列的值。数组存储相同类型的有序的值。字典序集合存储相同类型的无序的值,这些值可以通过一个唯一值(就是key啦)来搜索获取到。
在Swift中,存储在数组和字典序集合的key和值的类型总是很明确的。这意味着你不能插入一个错误的类型到数组或者字典序集合中去。这也意味着在你从数组或者字典序集合遍历值的时候你是很明确这些值的类型的。Swift的明确类型集合确保了你在编写代码时,值的类型总是明确有效的,而且也确保了在开发环境中就能捕捉到类型错误。
当Swift中的数组是分配给常量或者变脸,或者传递给一个函数或者方法是,它的排列方式和其他的不一样。想要更多信息,请看Mutability of Collections and Assignment and Copy Behavior for Collection Types
数组
数组存储大量的值,这些值的类型一样而且有序排列。相同的值可以在数组中在不同地方出现多次。
Swift的数组存储的值是是明确的。他们不同于Objective-C的NSArray和NSMutableArray类,这写类是可以存储各种不同的对象,而且不必提供任何这些对象的性质。在Swift中,一个特定数组能存储明确的类型值,无论是显示注解类型,还是接口类型,不一定非得是类类型。举个例子:你创建一个Int类型的数组,那你就不能插入除Int类型以外的值。Swift数组是类型安全的,而且总是很明确的知道它所包含的值的类型。
数组类型简写语法
Swift数组类型全写法这样:Array<SomeType>,其中SomeType就是数组允许被存储的类型(xuan:就是java中的泛型)。你也可以简写一个数组这样:SomeType[]。虽然两种写法功能上是一样的,但是简写是你的首选,当你要指定一个类型的数组时,按指南这样来写是很有用的。
数组体
你可以初始化一个数组用一个数组体,它是一种简短的写法可以写入一个或者多个值来形成一个数组集合。一个数组体被要求写入一系列的值,多个用逗号分开,用中括号包含:
下面举个例子来创建一个名字叫shoppingList的数组,该数组用来存储一些String值:
// shoppingList被初始化了,包含了两个值
shoppingList变量被声明成“一个存放String的数组”,就像这样的写法String[]。因为这个特定的数组被指定了String类型,所有它只能储存String类型的值。这里,shoppingList数组被初始化进去了两个值("Eggs" 和 "Milk"),直接写在了数组体内。
shoppingList数组被申明成了一个变量(用 var申明),而不是一个常量(用 let申明)所以在下面的例子中我们可以添加更多的items到这个shoppingList数组中去。
上面的例子中,数组体只包含了两个String类型的值没有其他。这符合了shoppingList的类型变量定义(这个数组只能包含String类型的值),因此数组体允许被两个初始化的值来初始化shoppingList。
得益于Swift的类型推断,如果你用包含一些相同类型的值来初始化数组,那么你不必写数组的类型。在初始化shoppingList时,你可以用更简短的方式来代替如下:
因为在数组体里面所有的值的类型是相同的,Swift就能够推断shoppingList变量类型就是String[]类型。
读取和修改数组
你可以获取和修改数组,通过它的方法和属性,或者通过它的下标。
通过只读属性count来读取数组的长度:
// 打印出:"The shopping list contains 2 items."
通过一个布尔类型isEmpty属性检查数组的长度是否为0:
println ( "The shopping list is empty." )
} else {
println ( "The shopping list is not empty." )
}
// 打印出:"The shopping list is not empty."
你可以调用append方法在数组后面添加一个元素:
// shoppingList现在包含3个元素,有人TM现在居然在做煎饼
另外,还可以用(+=)操作符来把一个元素添加到数组末尾:
// shoppingList现在包含4个元素
你也可以用(+=)操作符来把一个同类型数组元素添加到另一个数组的末尾:
// shoppingList现在包含7个元素
你可以使用下标语法来遍历数组元素。你可以在下标中指定你所有获取数据的索引,这样你就可以立刻拿到你要的值:
// 第一个元素等于 "Eggs"
注意哦,数组的第一个元素的下标是0不是1哦,Swift中的数组下标总0开始。
你可以使用下标语法通过索引修改已经存在的值:
// 现在数组中的第一个元素是"Six eggs"而不是"Eggs"了
你可以用下标语法一次性的替换数组中的值,尽管要替换的值的个数和被替换的值的个数不一样,这样也可以的哦。下面的例子就是用"Bananas" and "Apples"去替换"Chocolate Spread", "Cheese", and "Butter":
// shoppingList现在包含了6个元素
你不能用下标语法给数组的末尾添加一个新的元素。如果你尝试着用下标语法去遍历或者添加一个值,而下标是超出数组界限的,那么就会报runtime错误。不过,你可以在操作之前可以先通过比较数组中的count值来判断下标是否越界。除了count属性是 0(这意味着数组是空的)的情况外,一个数组的最大下标是count - 1,因为数组下标是从 0开始计数的。
要在数组的指定位置插入一个元素,你可以调用数组的insert(atIndex:)方法:
// shoppingList现在有7个元素
// "Maple Syrup"是数组的第一个元素了
这样调用insert方法,可以把一个新的"Maple Syrup"插到数组所有元素前,并自己指定下标为0。
同样的,你可以通过调用removeAtIndex方法来删除一个元素。这个方法可以删除指定位置的元素,并且返回被删除的元素(当然如果没有用到这个值你可以忽略它):
// 数组的第0个元素被删除了
// shoppingList现在包含6个元素,没有了Maple Syrup
// mapleSyrup常量现在等于被删除的字符串"Maple Syrup"
当有元素被删除,空位就会被替补,所以现在数组的第0个元素再次变成了"Six eggs":
// 第一个元素现在等于"Six eggs"
If you want to remove the final item from an array, use the removeLast method rather than the removeAtIndex method to avoid the need to query the array’s count property. Like the removeAtIndex method, removeLast returns the removed item: 如果你想删除数组的最后一个元素,可以使用removeLast方法,而不是用removeAtIndex方法,这样可以避免查询数组的count属性。和removeAtIndex方法一样,removeLast也会返回被删除的元素:
// 数组的最后一个元素被删除了
// shoppingList现在包含5个元素,没有了cheese
// 常量apples现在等于字符串"Apples"
遍历数组
你可以用for-in循环遍历整个数组:
println (item )
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
If you need the integer index of each item as well as its value, use the global enumerate function to iterate over the array instead. The enumerate function returns a tuple for each item in the array composed of the index and the value for that item. You can decompose the tuple into temporary constants or variables as part of the iteration: 如果你即需要遍历数组的值同时也要得到对应的下标,可以使用全局函数enumerate来遍历数组。enumerate函数会返回一个由数组下标和值组成的一个元组。你可以分解这个元组到一个常量或者变量里,来作为迭代的一部分:
println ( "Item \(index + 1): \(value)" )
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
关于更多的for-in循环知识,请看Loop部分。
创建和初始化一个数组
你可以使用初始化语法创建一个确定类型的空数组(没有初始化任何值)。
println ( "someInts is of type Int[] with \(someInts.count) items." )
// 打印出: "someInts is of type Int[] with 0 items."
另外,上下文已经提供了类型信息,类似于函数的参数或者一个已经确定的变量或常量,你就可以用一个空的数组体来创建一个空的数组,写成[](一对中括号):
// someInts 现在包含1类型是Int的值
someInts = [ ]
// someInts 现在是一个空数组,但是它还是Int类型的数组
Swift数组还提供了一个构造器来初始化一个数组,这个数组包含了确定的元素个数,还提供所有的默认值。你可以传入新数组的元素个数(下面叫count的参数),还有可以传入一个默认值(下面的叫repeatedValue的参数):
// threeDoubles 是Double[]类型,并等于[0.0, 0.0, 0.0]
多亏了类型推断,当你在使用这个构造器的时候你不必为数组指定存储的类型,因为构造器会去你传入的默认值中推断:
// anotherThreeDoubles是Double[]类型, 并且等于[2.5, 2.5, 2.5]
var anotherThreeDoubles = Array (count : 3 , repeatedValue : 2.5 )
// anotherThreeDoubles是Double[]类型, 并且等于[2.5, 2.5, 2.5]
最后,你可以使用操作符(+)来把两个相同类型的数组合并成一个。合成的新数组会从那两个数组类型中推断出该数组是什么类型:
// sixDoubles是Double[]类型,并且等于[0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
字典序集合
字典序集合是一个可以储存多个相同类型值的容器。每个值都关联一个唯一key,这个key就是这个值的标识。不像数组元素,字典序集合中的元素没有特定的排序。如果你想用一个值的唯一标识来查找这个值那你就可以使用字典序集合,这就像一部正真的字典,用一个特定的字来查找它的定义。(xuan:这里的字典序集合相当于java中的Map啦)
Swift中的字典序集合的key和值的类型都是具体明确的。这就不同于Objective-C中NSDictionary和NSMutableDictionary类,它们可以使用不同类型的对象来作为它们的key和值,并且是不用提供这些对象的任何性质信息。在Swift中,一个特定的字典序集合可以存储的key和值是明确的,无论是显示的注解还是推断类型。
Swift中的字典序类型这样写:Dictionary<KeyType, ValueType>,其中KeyType是字典序集合中的key的值类型,ValueType是字典序中的值的值类型。
唯一限制就是KeyType必须要可哈希的--就是说它必须要提供一种方式使它们可以被独特的表示。所有的Swift中的基本类型(例如:字符串Stirng,整形Int,浮点Double,布尔Bool)都默认是可哈希的,所有的这些类型都可以被用来做字典序集合的key。没有被指定值的枚举成员也默认是可以被哈希的。
字典序集合体
你可以直接用一个字典序集合体初始化一个字典序集合。和前面定义一个数组体的语法一样。字典序集合体就是使用简略写法直接写一个或者多个的键值对来定义一个字典。
一个键值对是一个键和值的组合。在字典序集合体里面,每一个键值对总是用一个冒号把键和值分割。键值对的写法就想是一个列表,使用逗号分割,并被一对中括号[]包含着:
在下面的例子,将会创建一个字典来储存国际机场的名字。在这个字典里面,键是三个字的国际航空运送协会代码,以及它的值是机场的名称:
airport字典被定义为一个类型为Dictionary<String, String>,这意味这,这个字典的键类型是字符串String,和它的值的类型也是String。
airport字典是被定义为一个变量(使用 var标识符)而不是常量(使用 let标识符),所以在下面的例子可以直接添加元素。
airport字典使用一个包含两个键值对的字典序集合体来初始化。第一对由一个叫“TYO”的键和一个叫“Tokyo”的值,第二对有一个叫“DUB”的键和一个叫“Dublin”的值。
这个字典序集合包含两个,字符串:字符串对。这符合airport变量定义的类型(一个字典只包括字符串(String)键和字符串(String)值),所以在分配字字典序集合体的时候被允许作为airport字典的两个初始化元素。
和数组一样,如果你初始化一个字典的时候使用相同的类型,你可以不指明字典的类型。airport初始化可以用下面这个简略写法来代替:
因为所有的键在字面上都是相同的类型,同样,所有的值也是同样的类型,所以Swift可以推断为Dictionary<String, String>是airports字典的正确类型。
读取和修改字典序集合
你可以通过属性,方法或者下标来读取和修改字典。和数组一样,你使用只读的count属性来检查字典包含多少个元素。
// 打印出:"The dictionary of airports contains 2 items."
你可以使用下标语法给一个字典添加一个元素。使用合适类型作为新的键,并分配给它一个合适的值:
// airports 现在有3组元素
你也可以使用下标语法去改变一个特定键所关联的值:
//"LHR" 的值已经被改变为 "London Heathrow"
同样, 使用字典的updateValue(forKey:) 方法去设置或者更新一个特定键的值 . 和上面的下标例子一样, updateValue(forKey:) 方法如果键不存在则会新增它的值,如果键存在则会更新它的值, 和下标不一样是, updateValue(forKey:) 方法 如果更新时,会返回原来旧的值, 可以使用这个来判断是否发生了更新。
if let oldValue = airports. updateValue ( "Dublin International" , forKey : "DUB" ) {
println ( "The old value for DUB was \(oldValue)." )
}
// 打印出 "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." )
}
// 打印出:"The name of the airport is Dublin International."
你可以使用下标语法把他的值设置为nil,这样就可以移除这个键值对:
airports [ "APL" ] = "Apple International"
// "Apple International" 不是APL的真实机场,所以删除它
airports [ "APL" ] = nil
// APL 已经从字典中被移除
同样,从一个字典中移除一个键值对可以使用removeValueForKey方法,这个方法如果存在键所对应的值,则移除一个键值对,并返回被移除的值,否则返回nil。
println ( "The removed airport's name is \(removedValue)." )
} else {
println ( "The airports dictionary does not contain a value for DUB." )
}
// 打印出"The removed airport's name is Dublin International."
遍历字典序集合
你可以使用一个for-in循环来遍历字典的键值对。字典中的每一个元素都会返回一个元祖,你可以在循环部分分解这个元祖,并用临时变量或者常量来储存它。
println ( "\(airportCode): \(airportName)" )
}
// TYO: Tokyo
// LHR: London Heathrow
更多有关for-in 循环的信息, 参见 For Loops. 你也可以读取字典的keys属性或者values属性来遍历这个字典的键或值的集合。
println ( "Airport code: \(airportCode)" )
}
// Airport code: TYO
// Airport code: LHR
for airportName in airports. values {
println ( "Airport name: \(airportName)" )
}
// Airport name: Tokyo
// Airport name: London Heathrow
如果你需要一个接口来创建一个字典的键或者值的数组实例,你可以使用keys或者values属性来初始化一个数值。
// airportCodes is ["TYO", "LHR"]
let airportNames = Array (airports. values )
// airportNames is ["Tokyo", "London Heathrow"]
Swift中的字典类型是非序列化集合,如果你需要序列化取回key,值,或者键值对,遍历字典不具体叙述。
创建一个空字典
和数组一样,你可以使用确定类型的语法创建一个空的字典。
// namesOfIntegers 是一个空的 Dictionary<Int, String> 类型的字典
这个例子创建了一个Int,String类型的字典来储存可读性较好的整数值。它的键是Int类型,以及它们的值是String类型。 如果上下文中已经提供类型信息,可用一个字典序集合体创建一个空的字典,写作[;](由一对[]包含一个冒号:)
// namesOfIntegers现在包含1 个键值对
namesOfIntegers = [ : ]
// namesOfIntegers 是一个类型为Int, String的空字典。
在这个场景,Swift数组和字典类型是一个内置的集合。更多的内置类型和集合参见Generics
集合的可变性
数组和字典序集合存储大量的值通过一个简单的集合。如果你通变量的形式创建了一个数组或者字典序集合,那么整个集合是可变的。可变的意思是指你可以通过添加或者删除一个存在的元素来改变集合的长度(即存储的数量)。相反的,如果你把数组或字典序集合声明了一个常量,那这个数组或者字典序集合就是不可变的,它的长度也就不能变了。
对于字典序集合来说,不可变意味着你不能去修改已经存在的key对应的值。一旦他们值被确定,一个不可变字典序集合的内容就不可改变了。
然后,对于数组来说,不可变有一点点不同。你任然不能做任何会改变数组大小的操作,但是你可以添加设置一个新的值到一个已存在的位置。这使得当Swift的数组的长度确定时,能更好地优化数组的性能。
拥有可变行为的数组也影响着数组实例的分配和修改,更多内容参见Assignment and Copy Behavior for Collection Types.
上面的所有例子中,都创建了一个不可变集合,当数组长度不需要被改变时,这是比较好的做法。
这样做可以让Swift编译器优化你所创建的集合。
结尾
本文已全部完成,感谢Swift001小组:徐安。本文原创翻译,如转载请贴明出处。仅供学习娱乐,如商业用途请联系我们。