Swift - 數(shù)組、字典啃奴、集合

Swift語言提供 Arrays潭陪、SetsDictionaries 三種基本的集合類型用來存儲集合數(shù)據(jù)。數(shù)組(Arrays)是有序數(shù)據(jù)的集最蕾。集合(Sets)是無序無重復數(shù)據(jù)的集依溯。字典(Dictionaries)是無序的鍵值對的集。

CollectionTypes_intro_2x.png

Swift 語言中的 Arrays瘟则、SetsDictionaries 中存儲的數(shù)據(jù)值類型必須明確黎炉。這意味著我們不能把錯誤的數(shù)據(jù)類型插入其中。同時這也說明你完全可以對取回值的類型非常放心醋拧。

注意: Swift 的 Arrays慷嗜、SetsDictionaries 類型被實現(xiàn)為泛型集合

數(shù)組

數(shù)組是Swift中最普通的集合,數(shù)組是有序的容器丹壕,并且容器中的每一個元素都是相同的類型庆械, 我們可以使用下標對其直接進行訪問(又稱為隨機訪問),相同的值可以多次出現(xiàn)在一個數(shù)組的不同位置中。

注意:
SwiftArray 類型被橋接到 Foundation 中的 NSArray 類菌赖。更多關(guān)于在 FoundationCocoa 中使用 Array 的信息缭乘,參見 Using Swift with Cocoa and Obejective-C(Swift 4.1)使用 Cocoa 數(shù)據(jù)類型部分。

數(shù)組的簡單語法

public struct Array<Element> { }

寫Swift數(shù)組應該遵循像Array<Element>這樣的形式琉用,其中Element是這個數(shù)組中唯一允許存在的數(shù)據(jù)類型堕绩。我們也可以使用像[Element]這樣的簡單語法。盡管兩種形式在功能上是一樣的邑时,但是推薦[Element]寫法奴紧,Element是一個泛型表示可指定任意類型。

創(chuàng)建一個空數(shù)組

我們可以使用構(gòu)造語法來創(chuàng)建一個由特定數(shù)據(jù)類型構(gòu)成的空數(shù)組:

var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.”)
//打印 "someInts is of type [Int] with 0 items.”

注意晶丘,通過構(gòu)造函數(shù)的類型黍氮,someInts的值類型被推斷為[Int]

如果代碼上下文中已經(jīng)提供了類型信息,例如一個函數(shù)參數(shù)或者一個已經(jīng)定義好類型的常量或者變量沫浆,那么我們可以使用空數(shù)組語句創(chuàng)建一個空數(shù)組觉壶,它的寫法很簡單:[](一對空方括號):

someInts.append(3)   //someInts 現(xiàn)在包含一個 Int 值,值為3
someInts =  [] //someInts 現(xiàn)在是空數(shù)組件缸,但是仍然是 [Int] 類型的。

創(chuàng)建一個帶有默認值的數(shù)組

Swift中的Array類型還提供一個可以創(chuàng)建特定大小并且所有數(shù)據(jù)都被默認的構(gòu)造方法叔遂。我們可以把準備加入新數(shù)組的數(shù)據(jù)項數(shù)量(count)和適當類型的初始值(repeating)傳入數(shù)組構(gòu)造函數(shù):

var threeDoubles = Array(repeating: 0.0, count: 3)
//threeDoubles 是一種 [Double] 數(shù)組他炊,等價于 [0.0, 0.0, 0.0]

通過兩個數(shù)組相加創(chuàng)建一個數(shù)組

我們可以使用加法操作符(+)來組合兩種已存在的相同類型數(shù)組。新數(shù)組的數(shù)據(jù)類型會被從兩個數(shù)組的數(shù)據(jù)類型中推斷出來:

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
//anotherThreeDoubles 被推斷為 [Double]已艰,等價于 [2.5, 2.5, 2.5]
 
var sixDoubles = threeDoubles + anotherThreeDoubles
//sixDoubles 被推斷為 [Double]痊末,等價于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

用數(shù)組字面量構(gòu)造數(shù)組

我們可以使用數(shù)組字面量來進行數(shù)組構(gòu)造,這是一種用一個或者多個數(shù)值構(gòu)造數(shù)組的簡單方法哩掺。數(shù)組字面量是一系列由逗號分割并由方括號包含的數(shù)值:形如:[value 1, value 2, value 3]凿叠。下面這個例子創(chuàng)建了一個叫做shoppingList并且存儲String的數(shù)組:

var shoppingList: [String] = ["Eggs", "Milk"]

shoppingList已經(jīng)被構(gòu)造并且擁有兩個初始項。shoppingList變量被聲明為“字符串值類型的數(shù)組“嚼吞,記作[String]盒件。 因為這個數(shù)組被規(guī)定只有String一種數(shù)據(jù)結(jié)構(gòu),所以只有String類型可以在其中被存取舱禽。 在這里炒刁,shoppingList數(shù)組由兩個String值("Eggs" 和"Milk")構(gòu)造,并且由數(shù)組字面量定義誊稚。

注意:shoppingList數(shù)組被聲明為變量(var關(guān)鍵字創(chuàng)建)而不是常量(let創(chuàng)建)是因為以后可能會有更多的數(shù)據(jù)項被插入其中翔始。

在這個例子中,字面量僅僅包含兩個String值里伯。匹配了該數(shù)組的變量聲明(只能包含String的數(shù)組)城瞎,所以這個字面量的分配過程可以作為用兩個初始項來構(gòu)造shoppingList的一種方式。

由于Swift的類型推斷機制疾瓮,當我們用字面量構(gòu)造只擁有相同類型值數(shù)組的時候脖镀,我們不必把數(shù)組的類型定義清楚。shoppingList的構(gòu)造也可以這樣寫:

var shoppingList = ["Eggs", "Milk"]

因為所有數(shù)組字面量中的值都是相同的類型爷贫,Swift 可以推斷出[String]shoppingList中變量的正確類型认然。

訪問和修改數(shù)組

我們可以通過數(shù)組的方法和屬性來訪問和修改數(shù)組,或者使用下標語法漫萄,可以使用數(shù)組的只讀屬性count來獲取數(shù)組中的數(shù)據(jù)項數(shù)量:

print("The shopping list contains \(shoppingList.count) items.")
// 輸出 "The shopping list contains 2 items."

使用布爾屬性isEmpty作為一個縮寫形式去檢查count屬性是否為0:

if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}
// 打印 "The shopping list is not empty."

也可以使用append()方法在數(shù)組后面添加新的數(shù)據(jù)項:

shoppingList.append("Flour")
// shoppingList 現(xiàn)在有3個數(shù)據(jù)項

除此之外卷员,使用加法賦值運算符(+=)也可以直接在數(shù)組后面添加一個或多個擁有相同類型的數(shù)據(jù)項:

shoppingList += ["Baking Powder"]
// shoppingList 現(xiàn)在有四項了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 現(xiàn)在有七項了

可以直接使用下標語法來獲取數(shù)組中的數(shù)據(jù)項,把我們需要的數(shù)據(jù)項的索引值放在直接放在數(shù)組名稱的方括號中:

var firstItem = shoppingList[0]// 第一項是 "Eggs"

注意:第一項在數(shù)組中的索引值是0而不是1腾务。 Swift 中的數(shù)組索引總是從零開始毕骡。

我們也可以用下標來改變某個已有索引值對應的數(shù)據(jù)值:

shoppingList[0] = "Six eggs"
// 其中的第一項現(xiàn)在是 "Six eggs" 而不是 “Eggs"

還可以利用下標來一次改變一系列數(shù)據(jù)值,即使新數(shù)據(jù)和原有數(shù)據(jù)的數(shù)量是不一樣的。下面的例子把"Chocolate Spread"未巫,"Cheese"窿撬,和"Butter"替換為"Bananas"和 "Apples":

shoppingList[4...6] = ["Bananas", "Apples"]// shoppingList 現(xiàn)在有6項

注意:不可以用下標訪問的形式去在數(shù)組尾部添加新項。

數(shù)組的插入

調(diào)用數(shù)組的insert(_:at:)方法來在某個具體索引值之前添加數(shù)據(jù)項:

shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 現(xiàn)在有7項, "Maple Syrup" 現(xiàn)在是這個列表中的第一項

這次insert(_:at:)方法調(diào)用把值為"Maple Syrup"的新數(shù)據(jù)項插入列表的最開始位置叙凡,并且使用0作為索引值劈伴。

數(shù)組的刪除

類似的我們可以使用remove(at:)方法來移除數(shù)組中的某一項。這個方法把數(shù)組在特定索引值中存儲的數(shù)據(jù)項移除并且返回這個被移除的數(shù)據(jù)項(我們不需要的時候就可以無視它):

shoppingList.remove(at: 0)
// 索引值為0的數(shù)據(jù)項被移除
// shoppingList現(xiàn)在只有6項握爷,而且不包括Maple Syrup;
// mapleSyrup常量的值等于被移除數(shù)據(jù)項的值 "Maple Syrup"

注意:如果我們試著對索引越界的數(shù)據(jù)進行檢索或者設置新值的操作跛璧,會引發(fā)一個運行期錯誤。

我們可以使用索引值和數(shù)組的count屬性進行比較來在使用某個索引之前先檢驗是否有效新啼。除了當count等于0 時(說明這是個空數(shù)組)追城,最大索引值一直是count - 1,因為數(shù)組都是零起索引燥撞。

數(shù)據(jù)項被移除后數(shù)組中的空出項會被自動填補座柱,所以現(xiàn)在索引值為0的數(shù)據(jù)項的值再次等于"Six eggs":

firstItem = shoppingList[0]
// firstItem 現(xiàn)在等于"Six eggs"

如果我們只想把數(shù)組中的最后一項移除,可以使用removeLast()方法而不是remove(at:)方法來避免我們需要獲取數(shù)組的count屬性物舒。就像后者一樣色洞,前者也會返回被移除的數(shù)據(jù)項:

let apples = shoppingList.removeLast()
// 數(shù)組的最后一項被移除了
// shoppingList現(xiàn)在只有5項,不包括Apples
// apples常量的值現(xiàn)在等于 "Apples" 字符串

數(shù)組的遍歷

我們可以使用for-in循環(huán)來遍歷所有數(shù)組中的數(shù)據(jù)項:

for item in shoppingList {
   print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas

如果我們同時需要每個數(shù)據(jù)項的值和索引值茶鉴,可以使用enumerated()方法來進行數(shù)組遍歷锋玲。enumerated()返回一個由每一個數(shù)據(jù)項索引值和數(shù)據(jù)值組成的元組。我們可以把這個元組分解成臨時常量或者變量來進行遍歷:

for (index, value) in shoppingList.enumerated() {
   print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas

遍歷數(shù)組涵叮,但是不包括第一個元素

for x in shoppingList.dropFirst(){
 print(x) //
}
 
/*
 Milk
 Flour
 Baking Powder
 Bananas
 */

遍歷數(shù)組惭蹂,但是不包括最后一個或者幾個元素

for x in shoppingList.dropLast(){
 print(x)
}
/*
 Six eggs
 Milk
 Flour
 Baking Powder
 */
for x in shoppingList.dropLast(3){
    print(x)
}
/*
 Six eggs
 Milk
 */
數(shù)組的可變性

舉個例子,要創(chuàng)建一個數(shù)字的數(shù)組割粮,我們可以這么寫:

// 斐波拉契數(shù)列
let fibs = [0, 1, 1, 2, 3, 5]

如果我們使用像append這樣的方法來修改上面定義的數(shù)組盾碗,會得到編譯錯誤。因為在上面的代碼中數(shù)組是用let生命為常量的舀瓢。在很大情景下廷雅,這是正常的做法,它可以避免我們不小心對數(shù)組作出改變京髓。如果我們想按照變量的方式來使用數(shù)組航缀,我們需要使用var來進行定義,而且很容易添加單個或者一系列元素

var mutableFibs = [0, 1, 1, 2, 3, 5]

mutableFibs.append(8)
mutableFibs.append(contentsOf: [13,21,34])

區(qū)別使用var和let可以給我們帶來不少好處堰怨,使用let定義的變量因為其具有不變性芥玉,因此更有理由被優(yōu)先使用,當你讀到類似let fib=...這樣的聲明時备图,你可以確定fibs的值將永遠不變灿巧,這一點是由編譯器強制保證的赶袄。

不過,要注意這只針對那些具有值語義的類型抠藕。使用let定義的類實例對象(也就是說對于引用類型)時饿肺,它保證的是這個引用永遠不會發(fā)生變化,你不能再給這個引用賦一個新的值,但是這個引用所指向的對象卻是可以改變的

數(shù)組是值類型

數(shù)組和標準庫中的所有集合類型一樣达传,是具有值語義的。當你創(chuàng)建一個新的數(shù)組變量并且把一個已經(jīng)存在的數(shù)組賦值給它時购岗,這個數(shù)組的內(nèi)容會被賦值。

舉個例子门粪,在下面的代碼中,x將不會被更改:

var x = [1,2,3]
var y = x
y.append(4)
y // 1,2,3,4
x // 1,2,3

var y=x語句復制了x烹困,所以在4添加到y末尾的時候玄妈,x并不會發(fā)生改變,它的值依然是[1,2,3]髓梅。當你把數(shù)組傳遞給一個函數(shù)時拟蜻,會發(fā)生同樣的事情;方法將得到這個數(shù)組的一份本地復制枯饿,所有對它的改變都不會影響調(diào)用者所持有的數(shù)組

對比一下Foundation框架中NSArray在可變特性上的處理方法酝锅,NSArray中沒有更改方法,想要更改一個數(shù)組奢方,你必須使用NSMutableArray搔扁。但是,就算你擁有的是一個不可變的NSArray蟋字,但是它的引用特性并不能保證這個數(shù)組不會被改變稿蹲。

let a = NSMutableArray(array: [1,2,3])
let b: NSArray = a // 不想讓b發(fā)生改變
a.insert(4, at: 3) // 但是事實上b的改變依然能夠被a影響
b // 1,2,3,4

正確的方式是在賦值時,先收到進行復制

let c = NSMutableArray(array: [1,2,3])
let b: NSArray = c.copy() as! NSArray // 不想讓b發(fā)生改變
c.insert(4, at: 3) // 1,2,3,4
b // 1,2,3

該例子中顯而易見鹊奖,我們需要進行復制苛聘,因為a的聲明畢竟是可變的,但是當把數(shù)組在方法和函數(shù)之間來回傳遞的時候忠聚,事情可能就不那么明顯了设哗。

而在Swift中,相較于NSArrayNSMutableArray兩種類型两蟀,數(shù)組只有嚴重統(tǒng)一的類型网梢,那就是Array。使用var可以將數(shù)組定義為可變垫竞,但是區(qū)別與NS的數(shù)組澎粟,當你使用let定義第二個數(shù)組,并將第一個數(shù)組賦值給它徐裸,也可以保證這個新的數(shù)組是不會改變的,因為這里沒有公用的引用

創(chuàng)建如此多的復制有可能造成性能問題气笙,不過實際上Swift標準庫中的所有集合類型都使用了“寫時復制”這一技術(shù),它能夠保證只在必要的時候?qū)?shù)據(jù)進行復制谭期。在上面的例子中,y.append被調(diào)用之前胀瞪,x和y都將共享內(nèi)部的存儲

Swift數(shù)組提供了你能想到的所有常規(guī)操作,像是isEmpty或是count幔摸。數(shù)組也允許直接使用特定的下標直接訪問其中的元素,像是fib[3]患雇。不過要注意:在使用下標獲取元素之前,需要確保索引值沒有超出范圍翠储,否則會導致程序奔潰

數(shù)組變形

對數(shù)組中的每個值指向轉(zhuǎn)換是一個很常見的任務庐舟。常用操作:創(chuàng)建一個新數(shù)組,對已有數(shù)組中的元素進行循環(huán)依次取出其中元素杠娱,對取出的元素進行操作,并把操作的結(jié)果加入到新數(shù)組的末尾睹簇。比如:下面的代碼計算了一個整型數(shù)組里元素的平方

var squared: [Int] = []
for fib in fibs {
    squared.append(fib * fib)
}
squared // 0,1,1,4,9,25

Swift數(shù)組擁有map方法疲憋,這個方法來自函數(shù)式編程的世界缚柳,專門用于對數(shù)組中的元素進行遍歷操作

public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

map 方法接受一個閉包作為參數(shù)构舟, 然后它會遍歷整個數(shù)組狗超,并對數(shù)組中每一個元素執(zhí)行閉包中定義的操作苦蒿。 相當于對數(shù)組中的所有元素做了一個映射,這是非常普遍的操作

let squared = fibs.map { $0 * $0}
squared // 0,1,1,4,9,25

使用map函數(shù)的優(yōu)勢

  • 代碼很短灸姊,代碼長度短意味著錯誤少,不過更重要的是夯膀,它比原來更清晰了。所有無關(guān)的內(nèi)容都被移除了,一旦習慣了map的使用押袍,就會發(fā)現(xiàn)map就像一個信號侮东,一旦看見它驱敲,就會知道即將有一個函數(shù)被作用在數(shù)組的每一個元素上,并返回一個數(shù)組,它將包含所有被轉(zhuǎn)換后的結(jié)果

  • squared將由map的結(jié)果得到姚建,我們不會再改變它的值掸冤,所以也就不再需要用var來進行聲明了铅匹,我們可以將其聲明為let罗丰。另外,由于數(shù)組元素的類型可以從傳遞給map的函數(shù)中推斷出來再姑,我們也不再需要為squared顯示地指明類型了

  • 創(chuàng)建map函數(shù)并不難萌抵,只需要把for循環(huán)模版部分用一個泛型函數(shù)封裝起來就可以了,下面是一種可能的實現(xiàn)方式

extension Array {
    func map<T>(_ transform: (Element) -> T) -> [T] {
        var result: [T] = []
        result.reserveCapacity(count)
        for x in self {
            result.append(transform(x))
        }
        return result
    }
}

Element是數(shù)組包含元素類型的占位符元镀,T是元素轉(zhuǎn)換之后的類型占位符绍填。map函數(shù)本身并不關(guān)心
ElementT究竟是什么,它們可以是任意類型。T的具體類型將由調(diào)用者傳入給maptransform方法的返回值類型來決定。

index函數(shù)

找到具體元素的位置,第一次出現(xiàn)的位置

if let index = array.index(where: { element -> Bool in return element == 4 }) {
  print("index = \(index)") //index = 2
}

另外一個例子:

let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
if let i = students.index(where: { $0.hasPrefix("A") }) {
    print("\(students[i]) starts with 'A'!")
}
// Prints "Abena starts with 'A'!"
Filter

將數(shù)組中符合一定條件的元素過濾出來并用它們創(chuàng)建一個新的數(shù)組氢拥。對數(shù)組進行循環(huán)并且根據(jù)條件過濾其中元素的模式深滚。

let cast = ["Vivien", "Marlon", "Kim", "Karl"]
let shortNames = cast.filter { $0.characters.count < 5 }
print(shortNames)
// Prints "["Kim", "Karl"]"

filter內(nèi)部實現(xiàn)

extension Array {
    func filter(_ isIncluded: (Element) -> Bool) -> [Element] {
        var result: [Element] = []
        for x in self where isIncluded(x) {
            result.append(x)
        }
        return result
    }
}

組合map,filter寫出更簡單的表達根吁,如:查找100以下所有的偶數(shù)

let res = (1..<10).map{$0 * $0}.filter{$0 % 2 == 0}
print(res) //[4, 16, 36, 64]
reduce函數(shù)

對于map,filter都是在一個數(shù)組的基礎上產(chǎn)生一個新數(shù)組或者修改數(shù)組轰枝,然后有時候寿烟,你想組合所有的元素到一個新值内边。例如:計算所有元素的和莹汤,我們能夠使用如下代碼:

let number = [0,1,1,2,3,4,5]
var total = 0
for num in number {
    total = total + num
}
print("total = \(total)") //total = 16

reduce方法使用了這種模式,并且抽象為兩個部分,一個初始值红伦,一個是函數(shù)用于組合中間值和元素值陵吸,使用reduce剩拢,我們的代碼如下:

let sum = number.reduce(0){ total, num in total + num}
//因為操作符+也是函數(shù)饶唤,所以我們可以直接使用+號
let shortSum = fibs.reduce(0, +)
print("sum = \(sum),shortSum = \(shortSum)") //sum = 16,shortSum = 16

注意:輸出的結(jié)果類型并不一定跟元素的類型一樣祸穷,例如:我們想轉(zhuǎn)換integerstring

let stringArray = number.reduce(""){str, num in str + "\(num)"}
print("stringArray = \(stringArray)") //stringArray = 0112345

reduce的內(nèi)部實現(xiàn)

extension Array {
    func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) -> Result) -> Result {
        var result = initialResult
        for x in self {
            result = nextPartialResult(result, x)
        }
        return result
    }
}
flatMap

通過為序列中的每一個元素進行轉(zhuǎn)換,即執(zhí)行閉包衰倦,返回的數(shù)組是包含串行的結(jié)果.

let numbers = [1, 2, 3, 4]
let mapped = numbers.map { Array(repeating: $0, count: $0) }
print("mapped = \(mapped)") //mapped = [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]

let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
print("flatMapped = \(flatMapped)") //flatMapped = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

flatMap組合元素來自不同的數(shù)組

let suits = ["?", "?", "?", "?"]
let ranks = ["J","Q","K","A"]
let results = suits.flatMap { suit in
    ranks.map { rank in
        (suit,rank)
    }
}
print("results = \(results)")
//results = [("?", "J"), ("?", "Q"), ("?", "K"), ("?", "A"), ("?", "J"), ("?", "Q"), ("?", "K"), ("?", "A"), ("?", "J"), ("?", "Q"), ("?", "K"), ("?", "A"), ("?", "J"), ("?", "Q"), ("?", "K"), ("?", "A")]

flatMap內(nèi)部實現(xiàn)

extension Array {
    func flatMap<T>(_ transform: (Element) -> [T]) -> [T] {
        var result: [T] = []
        for x in self {
            result.append(contentsOf: transform(x))
        }
        return result
    }
}
forEach

forEach,就像一個循環(huán)我磁,傳遞一個函數(shù)功能用于執(zhí)行序列中的每一個元素孽文。但是不像mapforEach并不返回任何值夺艰。

for element in [1,2,3] {
   print(element)
}
 
[1,2,3].forEach { element in
    print(element)
}

上下對比一下也沒有看到優(yōu)勢芋哭,但是它能夠隨意使用,如果你想為集合執(zhí)行單一功能的操作郁副,傳遞一個函數(shù)名給forEach而不是一個比包表達式减牺,這樣能夠看起來更加簡單和精準的代碼。例如:在你的視圖控制器之內(nèi)存谎,你想添加子視圖( subviews)的數(shù)組到主視圖(main view)烹植,僅僅只需要 theViews.forEach(view.addSubview)

然而,對于for循環(huán)和forEach還是存在一些區(qū)別的愕贡,例如:如果使用for循環(huán)執(zhí)行返回一條語句草雕,使用forEach重寫將更好。對于多語句的遍歷固以,不要使用forEach墩虹。

數(shù)組的切片

除了通過單獨的下標來訪問數(shù)組的元素(fibs[0]),還可以通過下標來獲取某個范圍中的元素憨琳。比如诫钓,想要獲取除了首個元素的其他元素

let slice = fibs[1..<fibs.endIndex]
slice // 1,1,2,3,5
type(of: slice) // ArraySlice<Int>

它將返回一個數(shù)組切片(slice),其中包含了愿數(shù)組中從第二個元素到最后一個元素的數(shù)據(jù)篙螟。得到的結(jié)果類型是ArraySlice而不是Array菌湃。

切片類型只是數(shù)組的一種表示方式,它背后的數(shù)據(jù)仍然是原來的數(shù)組遍略,只不過是用切片的方式來進行表示惧所。

字典

字典是一種存儲多個相同類型的值的容器。每個值(value)都關(guān)聯(lián)唯一的鍵(key)绪杏,鍵作為字典中的這個值數(shù)據(jù)的標識符下愈。和數(shù)組中的數(shù)據(jù)項不同,字典中的數(shù)據(jù)項并沒有具體順序蕾久。我們在需要通過標識符(鍵)訪問數(shù)據(jù)的時候使用字典势似,這種方法很大程度上和我們在現(xiàn)實世界中使用字典查字義的方法一樣。

字典類型簡化語法

Swift中的字典使用Dictionary<Key, Value>定義履因,其中Key是字典中鍵的數(shù)據(jù)類型,Value是字典中對應于這些鍵所存儲值的數(shù)據(jù)類型栅迄。我們也可以用[Key: Value]這樣簡化的形式去創(chuàng)建一個字典類型。雖然這兩種形式功能上相同霞篡,但是后者是首選端逼。

注意:一個字典的Key類型必須遵循Hashable協(xié)議,就像Set的值類型顶滩。

創(chuàng)建一個空字典

我們可以像數(shù)組一樣使用構(gòu)造語法創(chuàng)建一個擁有確定類型的空字典:

var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一個空的 [Int: String] 字典

這個例子創(chuàng)建了一個[Int: String]類型的空字典來儲存整數(shù)的英語命名。它的鍵是Int型礁鲁,值是String型。如果上下文已經(jīng)提供了類型信息仅醇,我們可以使用空字典字面量來創(chuàng)建一個空字典冗美,記作[:](中括號中放一個冒號):

namesOfIntegers[16] = "sixteen"
// namesOfIntegers 現(xiàn)在包含一個鍵值對
namesOfIntegers = [:]
// namesOfIntegers 又成為了一個 [Int: String] 類型的空字典

字典的字面量

我們可以使用字典字面量來構(gòu)造字典,和數(shù)組字面量擁有相似語法析二。

字典字面量是一種將一個或多個鍵值對寫作Dictionary集合的快捷途徑粉洼。一個鍵值對是一個key和一個value的結(jié)合體。在字典字面量中叶摄,每一個鍵值對的鍵和值都由冒號分割属韧。這些鍵值對構(gòu)成一個列表,其中這些鍵值對由方括號包含蛤吓、由逗號分割:

[key1: value1, key2: value2, key3: value3]

下面的例子創(chuàng)建了一個存儲國際機場名稱的字典宵喂。在這個字典中鍵是三個字母的國際航空運輸相關(guān)代碼,值是機場名稱:

var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

airports字典被聲明為一種[String: String]類型会傲,這意味著這個字典的鍵和值都是String類型锅棕。

注意:airports字典被聲明為變量(用var關(guān)鍵字)而不是常量(let關(guān)鍵字)因為可能會有更多的機場信息會被添加到這個示例字典中。

airports字典使用字典字面量初始化淌山,包含兩個鍵值對哲戚。第一對的鍵是YYZ,值是Toronto Pearson艾岂。第二對的鍵是DUB顺少,值是Dublin。這個字典語句包含了兩個String: String類型的鍵值對王浴。它們對應airports變量聲明的類型(一個只有String鍵和String值的字典)所以這個字典字面量的任務是構(gòu)造擁有兩個初始數(shù)據(jù)項的airport字典脆炎。

和數(shù)組一樣,我們在用字典字面量構(gòu)造字典時氓辣,如果它的鍵和值都有各自一致的類型秒裕,那么就不必寫出字典的類型。airports字典也可以用這種簡短方式定義:

var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

因為這個語句中所有的鍵和值都各自擁有相同的數(shù)據(jù)類型钞啸,Swift 可以推斷出Dictionary<String, String>airports字典的正確類型几蜻。

訪問和修改字典

我們可以通過字典的方法和屬性來訪問和修改字典喇潘,或者通過使用下標語法。和數(shù)組一樣梭稚,我們可以通過字典的只讀屬性count來獲取某個字典的數(shù)據(jù)項數(shù)量:

print("The dictionary of airports contains \(airports.count) items.")
// 打印 "The dictionary of airports contains 2 items."(這個字典有兩個數(shù)據(jù)項)

使用布爾屬性isEmpty作為一個縮寫形式去檢查count屬性是否為0:

if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary is not empty.")
}
// 打印 "The airports dictionary is not empty."

我們也可以在字典中使用下標語法來添加新的數(shù)據(jù)項颖低』】荆可以使用一個恰當類型的鍵作為下標索引,并且分配恰當類型的新值:

airports["LHR"] = "London"
// airports 字典現(xiàn)在有三個數(shù)據(jù)項

我們也可以使用下標語法來改變特定鍵對應的值:

airports["LHR"] = "London Heathrow"
// "LHR"對應的值 被改為 "London Heathrow

作為另一種下標方法莺戒,字典的updateValue(_:forKey:)方法可以設置或者更新特定鍵對應的值从铲。

就像上面所示的下標示例食店,updateValue(_:forKey:)方法在這個鍵不存在對應值的時候會設置新值或者在存在時更新已存在的值吉嫩。

和上面的下標方法不同的是updateValue(_:forKey:)這個方法返回更新值之前的原值嗅定。這樣使得我們可以檢查更新是否成功渠退。

updateValue(_:forKey:)方法會返回對應值的類型的可選值。舉例來說:對于存儲String值的字典姊扔,這個函數(shù)會返回一個String?或者“可選 String”類型的值恰梢。如果有值存在于更新前嵌言,則這個可選值包含了舊值及穗,否則它將會是nil埂陆。

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \(oldValue).")
}
// 輸出 "The old value for DUB was Dublin."

我們也可以使用下標語法來在字典中檢索特定鍵對應的值。因為有可能請求的鍵沒有對應的值存在购裙,字典的下標訪問會返回對應值的類型的可選值缓窜。如果這個字典包含請求鍵所對應的值禾锤,下標會返回一個包含這個存在值的可選值恩掷,否則將返回nil:

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport is not in the airports dictionary.")
}
// 打印 "The name of the airport is Dublin Airport."

我們還可以使用下標語法來通過給某個鍵的對應值賦值為nil來從字典里移除一個鍵值對:

airports["APL"] = "Apple Internation"
// "Apple Internation" 不是真的 APL 機場, 刪除它
airports["APL"] = nil
// APL 現(xiàn)在被移除了

此外黄娘,removeValue(forKey:)方法也可以用來在字典中移除鍵值對克滴。這個方法在鍵值對存在的情況下會移除該鍵值對并且返回被移除的值或者在沒有值的情況下返回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.")
}
// prints "The removed airport's name is Dublin Airport."

字典遍歷

我們可以使用for-in循環(huán)來遍歷某個字典中的鍵值對劝赔。每一個字典中的數(shù)據(jù)項都以(key, value)元組形式返回着帽,并且我們可以使用臨時常量或者變量來分解這些元組:

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow

通過訪問keys或者values屬性仍翰,我們也可以遍歷字典的鍵或者值:

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
 
for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow

如果我們只是需要使用某個字典的鍵集合或者值集合來作為某個接受Array實例的 API 的參數(shù)予借,可以直接使用keys或者values屬性構(gòu)造一個新數(shù)組:

let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
 
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]

Swift的字典類型是無序集合類型。為了以特定的順序遍歷字典的鍵或值喧笔,可以對字典的keys或values屬性使用sorted()方法书闸。

合并兩個字典

合并兩個字典浆劲,用來做合并的字典需要覆蓋重復的鍵

擴展Dictionary類型,為它添加一個merge方法度气,該方法接受帶合并的字典作為參數(shù)膨报。我們可以將參數(shù)指明為Dictionary類型现柠,不過更好的選擇是用更加通用的泛型方法來進行實現(xiàn)

我們對參數(shù)的要求是够吩,它必須是一個序列,這樣我們就可以對其進行枚舉遍歷强法,另外饮怯,序列的元素必須是鍵值對硕淑,而且它必須接受調(diào)用的字典的鍵值值擁有相同的類型嘉赎。對于任意的Sequence公条,如果它的Iterator.Element(Key,Value)話靶橱,它就滿足我們的要求

extension Dictionary {
    mutating func merge<S>(_ other: S)  where S: Sequence,S.Iterator.Element == (key: Key,value: Value) {
        for (k,v) in other {
            self[k] = v
        }
    }
}

再擴展一個字典的初始化方法关霸,通過字典創(chuàng)建字典

extension Dictionary {
    init<S: Sequence>(_ sequence: S) where S.Iterator.Element == (key: Key,value: Value) {
        self = [:]
        self.merge(sequence)
    }
}

對字典的values進行映射

extension Dictionary {
    func mapValues<NewValue>(transform: (Value) -> NewValue) ->[Key: NewValue] {
        return Dictionary<Key,NewValue>(map { (key, value) in
            return (key, transform(value))
        })
    }
}
Hashable要求

字典其實是哈希表队寇,字典通過鍵的hashValue來為每一個鍵指定一個位置,以及它所對應的存儲识埋。這也就是Dictionary要求它的key類型需要遵守Hashable協(xié)議的原因窒舟。標準庫中所有的基本數(shù)據(jù)類型都是遵守Hashable協(xié)議的惠豺,它們包括字符串,整數(shù)蛹疯,浮點數(shù)以及布爾值苍苞。不帶關(guān)聯(lián)值的枚舉值類型也會自動遵守Hashable

如果想將自定義的類型用作字典的key狼纬,那么必須手動為自定義類型添加Hashable并滿足它疗琉,這需要你實現(xiàn)hashValue屬性盈简。另外柠贤,因為Hashable本身是對Equatable的擴展类缤,因此餐弱,還需要為自定義類型重載==運算符膏蚓。

實現(xiàn)必須保證哈希不變原則:兩個同樣的實例,必須擁有同樣的哈希值氓扛,反過來不必為真:兩個哈希值相同的實例不一定需要相等幢尚。不同的哈希值的數(shù)量是有限的,然后很多可以被哈希的類型(比如:字符串)的個數(shù)是無窮的真慢。

哈虾诮纾可能重復這一特性皂林,意味著Dictionary必須能夠處理哈希碰撞础倍。優(yōu)秀的哈希算法總是能給出較少的碰撞沟启,這將保持集合的性能特性。理想狀態(tài)下芽卿,我們希望得到的哈希值在整個整數(shù)范圍內(nèi)均勻分布卸例。在極端的例子下筷转,如果你的實現(xiàn)對所有實例返回相同的哈希值呜舒,那么這個字典的查找性能下降到O(n)

優(yōu)秀哈希算法的第二個特質(zhì)是它應該很快摊滔,記住艰躺,在字典中進行插入腺兴,刪除,或者查找時篓足,這些哈希值都要被計算栈拖。如果你的hashValue實現(xiàn)要消耗太多時間涩哟,那么它很可能會拖慢你的程序,讓你的字典從O(1)特性中得到的好處損失殆盡

下一個能同時做到這些要求的哈希算法并不容易潜腻,對于一些由本身就是Hashable的數(shù)據(jù)類型組成的類型來說融涣,將成員的哈希值進行“異或”運算往往是一個不錯的起點

struct Person {
    var name: String
    var zipCode: Int
    var birthday: Date
}

extension Person: Equatable {
    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.zipCode == rhs.zipCode && lhs.birthday == rhs.birthday
    }
}

extension Person: Hashable {
    var hashValue: Int {
        return name.hashValue ^ zipCode.hashValue ^ birthday.hashValue
    }
}

異或計算方法的一個限制是威鹿,這個操作本身是左右對稱的(也就是說a^b == b^a)杂拨,對于某些數(shù)據(jù)的哈希計算悯衬,這有時候會造成不必要的碰撞筋粗,你可以添加一個位旋轉(zhuǎn)并混合使用它們來避免這個問題

最后娜亿,當你使用不具有值語義的類型(比如可變的對象)作為字典的鍵時,需要特別小心沛婴。如果你在將一個對象用做字典的鍵后嘁灯,改變了它的內(nèi)容躲舌,它的哈希值/或相等特性往往也會發(fā)生改變,這時候你將無法再在字典中找到它秒旋,這時字典在錯誤的位置存儲對象迁筛,這將導致字典內(nèi)部存儲錯誤耕挨,對于值類型來說,因為字典中的鍵不會和復制的值共用存儲酒甸,因此它也不會被從外部改變插勤,所以不存在這個問題

Set

集合(Set)用來存儲相同類型并且沒有確定順序的值农尖。當集合元素順序不重要時或者希望確保每個元素只出現(xiàn)一次時可以使用集合而不是數(shù)組良哲。

注意
Swift 的 Set 類型被橋接到 Foundation 中的 NSSet 類筑凫。
關(guān)于使用 FoundationCocoaSet 的知識,參見 Using Swift with Cocoa and Obejective-C(Swift 4.1)使用 Cocoa 數(shù)據(jù)類型部分滓技。

集合類型的哈希值

一個類型為了存儲在集合中令漂,該類型必須是可哈系兀化的--也就是說妹窖,該類型必須提供一個方法來計算它的哈希值嘱吗。一個哈希值是Int類型的,相等的對象哈希值必須相同哆致,比如a==b,因此必須a.hashValue == b.hashValue患膛。

Swift 的所有基本類型(比如String,Int,Double和Bool)默認都是可哈献俚牛化的跃捣,可以作為集合的值的類型或者字典的鍵的類型。沒有關(guān)聯(lián)值的枚舉成員值(在枚舉有講述)默認也是可哈虾ㄕ停化的闻镶。

注意:

你可以使用你自定義的類型作為集合的值的類型或者是字典的鍵的類型丸升,但你需要使你的自定義類型符合 Swift標準庫中的Hashable協(xié)議。符合Hashable協(xié)議的類型需要提供一個類型為Int的可讀屬性hashValue墩剖。由類型的hashValue屬性返回的值不需要在同一程序的不同執(zhí)行周期或者不同程序之間保持相同涛碑。

public struct Set<Element> where Element : Hashable {}

public protocol Hashable : Equatable {
    public var hashValue: Int { get }
    public func hash(into hasher: inout Hasher)
}

public protocol Equatable {
    public static func == (lhs: Self, rhs: Self) -> Bool
}

因為Hashable協(xié)議符合Equatable協(xié)議孵淘,所以遵循該協(xié)議的類型也必須提供一個"是否相等"運算符(==)的實現(xiàn)瘫证。這個Equatable協(xié)議要求任何符合==實現(xiàn)的實例間都是一種相等的關(guān)系背捌。也就是說毡庆,對于a,b,c三個值來說么抗,==的實現(xiàn)必須滿足下面三種情況:

a == a(自反性)
a == b意味著b == a(對稱性)
a == b && b == c意味著a == c(傳遞性)

集合類型語法

Swift中的Set類型被寫為Set<Element>,這里的Element表示Set中允許存儲的類型螟加,和數(shù)組不同的是捆探,集合沒有等價的簡化形式黍图。

創(chuàng)建一個空的集合

你可以通過構(gòu)造器語法創(chuàng)建一個特定類型的空集合:

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// 打印 "letters is of type Set<Character> with 0 items."

注意:通過構(gòu)造器雌隅,這里的letters變量的類型被推斷為Set<Character>

此外恰起,如果上下文提供了類型信息检盼,比如作為函數(shù)的參數(shù)或者已知類型的變量或常量吨枉,我們可以通過一個空的數(shù)組字面量創(chuàng)建一個空的Set

letters.insert("a")
// letters 現(xiàn)在含有1個 Character 類型的值
letters = []
// letters 現(xiàn)在是一個空的 Set, 但是它依然是 Set<Character> 類型

用數(shù)組字面量創(chuàng)建集合

你可以使用數(shù)組字面量來構(gòu)造集合貌亭,并且可以使用簡化形式寫一個或者多個值作為集合元素圃庭。

下面的例子創(chuàng)建一個稱之為favoriteGenres的集合來存儲String類型的值:

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被構(gòu)造成含有三個初始值的集合

這個favoriteGenres變量被聲明為一個String值的集合”失晴,寫為Set<String>涂屁。由于這個特定的集合含有指定String類型的值拆又,所以它只允許存儲String類型值。這里的favoriteGenres變量有三個String類型的初始值("Rock"发笔,"Classical"和"Hip hop")了讨,并以數(shù)組字面量的方式出現(xiàn)前计。

注意:favoriteGenres被聲明為一個變量(擁有var標示符)而不是一個常量(擁有let標示符),因為它里面的元素將會在下面的例子中被增加或者移除男杈。

一個Set類型不能從數(shù)組字面量中被單獨推斷出來伶棒,因此Set類型必須顯式聲明肤无。然而宛渐,由于 Swift的類型推斷功能窥翩,如果你想使用一個數(shù)組字面量構(gòu)造一個Set并且該數(shù)組字面量中的所有元素類型相同,那么你無須寫出Set的具體類型鳞仙。favoriteGenres的構(gòu)造形式可以采用簡化的方式代替:

var favoriteGenres1: Set = ["Rock", "Classical", "Hip hop"]
//由于數(shù)組字面量中的所有元素類型相同寇蚊,Swift 可以推斷出Set<String>作為favoriteGenres變量的正確類型。

訪問和修改一個集合

你可以通過Set的屬性和方法來訪問和修改Set棍好。為了找出Set中元素的數(shù)量仗岸,可以使用其只讀屬性count:

print("I have \(favoriteGenres.count) favorite music genres.")
// 打印 "I have 3 favorite music genres."

使用布爾屬性isEmpty作為一個縮寫形式去檢查count屬性是否為0:

if favoriteGenres.isEmpty {
    print("As far as music goes, I'm not picky.")
} else {
    print("I have particular music preferences.")
}
// 打印 "I have particular music preferences."

你可以通過調(diào)用Set的insert(_:)方法來添加一個新元素:

favoriteGenres.insert("Jazz")
// favoriteGenres 現(xiàn)在包含4個元素

你可以通過調(diào)用Setremove(_:)方法去刪除一個元素,如果該值是該Set的一個元素則刪除該元素并且返回被刪除的元素值梳玫,否則如果該Set不包含該值爹梁,則返回nil。另外提澎,Set中的所有元素可以通過它的removeAll()方法刪除。

if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? I'm over it.")
} else {
    print("I never much cared for that.")
}
// 打印 "Rock? I'm over it."

使用contains(_:)方法去檢查Set中是否包含一個特定的值:

if favoriteGenres.contains("Funk") {
    print("I get up on the good foot.")
} else {
    print("It's too funky in here.")
}
// 打印 "It's too funky in here."

遍歷一個集合

你可以在一個for-in循環(huán)中遍歷一個Set中的所有值盼忌。

for genre in favoriteGenres {
    print("\(genre)")
}
// Classical
// Jazz
// Hip hop

SwiftSet類型沒有確定的順序,為了按照特定順序來遍歷一個Set中的值可以使用sorted()方法川慌,它將返回一個有序數(shù)組,這個數(shù)組的元素排列順序由操作符<對元素進行比較的結(jié)果來確定.

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// prints "Classical"
// prints "Hip hop"
// prints "Jazz

集合操作

你可以高效地完成Set的一些基本操作,比如把兩個集合組合到一起,判斷兩個集合共有元素,或者判斷兩個集合是否全包含于置,部分包含或者不相交瞄桨。

基本集合操作

下面的插圖描述了兩個集合-a和b-以及通過陰影部分的區(qū)域顯示集合各種操作的結(jié)果。

20170422075031062.png

1 使用intersection(_:)方法根據(jù)兩個集合中都包含的值創(chuàng)建的一個新的集合。
2 使用symmetricDifference(_:)方法根據(jù)在一個集合中但不在兩個集合中的值創(chuàng)建一個新的集合。
3 使用union(_:)方法根據(jù)兩個集合的值創(chuàng)建一個新的集合宽菜。
4 使用subtracting(_:)方法根據(jù)不在該集合中的值創(chuàng)建一個新的集合烈菌。

let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
 
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

集合成員關(guān)系和相等

下面的插圖描述了三個集合-a,b和c,以及通過重疊區(qū)域表述集合間共享的元素。集合a是集合b的父集合,因為a包含了b中所有的元素,相反的举反,集合b是集合a的子集合雕崩,因為屬于b的元素也被a包含粗蔚。集合b和集合c彼此不關(guān)聯(lián)当辐,因為它們之間沒有共同的元素寺晌。

20170422075428988.png

1 使用“是否相等”運算符(==)來判斷兩個集合是否包含全部相同的值陆赋。
2 使用isSubset(of:)方法來判斷一個集合中的值是否也被包含在另外一個集合中灾锯。
3 使用isSuperset(of:)方法來判斷一個集合中包含另一個集合中所有的值凌那。
4 使用isStrictSubset(of:)或者isStrictSuperset(of:)方法來判斷一個集合是否是另外一個集合的子集合或者。父集合并且兩個集合并不相等。
5 使用isDisjoint(with:)方法來判斷兩個集合是否不含有相同的值(是否沒有交集)。

let a: Set = ["1","2","3","4","5","6"]
let b: Set = ["1","2"]
let c: Set = ["7"]
 
b.isSubset(of: a)  // true
a.isSuperset(of: b) // true
a.isDisjoint(with: c) // true

Set擴展

集合唯一和有序性

Sequence添加一個擴展,用于獲取Sequence中所有唯一的元素髓绽。因為我們是很容易將所有的元素放到Set中,并且返回內(nèi)容,但是這并不穩(wěn)定,因為Set的順序是未定義的藤滥,為了保證輸入元素的順序和唯一性标沪。進行如下擴展

extension Sequence where Iterator.Element: Hashable {
    func unique() -> [Iterator.Element] {
        var seen: Set<Iterator.Element> = []
        return filter({
            if seen.contains($0) {
               return false
            }else {
               seen.insert($0)
                return true
            }
        })
    }
}
 
[1,2,3,12,1,3,4,5,6,4,6].unique() // [1, 2, 3, 12, 4, 5, 6]

數(shù)組和集合的對比

  • Set用于無序的唯一對象币他,Array是有序的并且可以包含重復數(shù)據(jù)
  • Array迭代速度比Set看尿这,Set搜索速度比Array快

參考

《Swift進階》
Collection Types

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罗洗,一起剝皮案震驚了整個濱河市典奉,隨后出現(xiàn)的幾起案子陕靠,更是在濱河造成了極大的恐慌溉躲,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件具钥,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機嗅绸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門麻蹋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诺苹,你說我怎么就攤上這事势篡。” “怎么了濒旦?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵雕什,是天一觀的道長。 經(jīng)常有香客問我七嫌,道長骂维,這世上最難降的妖魔是什么猴誊? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任澄成,我火速辦了婚禮卫漫,結(jié)果婚禮上镐确,老公的妹妹穿的比我還像新娘嚷狞。我一直安慰自己持隧,他們只是感情好十饥,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蜒秤,像睡著了一般汁咏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上作媚,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天攘滩,我揣著相機與錄音,去河邊找鬼掂骏。 笑死轰驳,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的弟灼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼冒黑,長吁一口氣:“原來是場噩夢啊……” “哼田绑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抡爹,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤掩驱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后冬竟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欧穴,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑰剃。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盔几,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慨菱,到底是詐尸還是另有隱情残揉,我是刑警寧澤昏苏,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布弦叶,位于F島的核電站俊犯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伤哺。R本人自食惡果不足惜燕侠,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望立莉。 院中可真熱鬧绢彤,春花似錦、人聲如沸桃序。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽媒熊。三九已至奇适,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芦鳍,已是汗流浹背嚷往。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柠衅,地道東北人皮仁。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像菲宴,于是被迫代替她去往敵國和親贷祈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容