【Swift进阶笔记】内建集合类型-字典

2021-11-02  本文已影响0人  BeethOven
image.png

特征

字典典是无序的,使用 for 循环来枚举字典中的键值对时,顺序是不确定的

一个app的设置界面,使用enum来表示

enum Setting {
  case text(String)
  case int(Int)
  case bool(Bool)
}
let defaultSetting: [String: Setting] = [
  "Open": .bool(false),
  "Name": .text("My iPhone")
]
defaultSetting["Name"]

通过字典查找的值总是返回一个可选值,当它不存在时,它就会返回nil.数组越界则会崩溃

“与通常使用的以键为参数的下标方法形成对比的是,作为实现 Collection 协议的一部分,字典也有一个接受索引的下标方法。就像数组一样,当用一个无效的索引调用这个方法时,程序会崩溃”

可变性

你可以向var定义的可变字典移除一个值,通过用下标将对应的值设置为nil,要么调用removeValue(forKey:)。后一种方法还会将被删除的值返回 (如果待删除的键不存在,则返回 nil)。对于一个用let定义的不可变的字典,想要进行修改的话,我们需要进行复制

var userSettings = defaultSettings
userSettings["Name"] = .text("Jared's iPhone")
userSettings["Do Not Disturb"] = .bool(true)

defaultSettings在这里没有过改变,也可以用updateValue(_:forKey:)更新值,这个方法会返回之前的值

一些有用的字典方法

merge(_:uniquingKeysWith:)

合并俩个字典

var settings = defaultSettings
let overriddenSettings: [String:Setting] = ["Name": .text("Jane's iPhone")]
settings.merge(overriddenSettings, uniquingKeysWith: { $1 })
settings
// ["Airplane Mode": Setting.bool(false), "Name": Setting.text("Jane\'s iPhone")]

mapValues

对值进行变换

“let settingsAsStrings = settings.mapValues { setting -> String in
switch setting {
case .text(let text): return text
case .int(let number): return String(number)
case .bool(let value): return String(value)
}
}
settingsAsStrings // ["Airplane Mode": "false", "Name": "Jane\'s iPhone"]”

摘录来自: Chris Eidhof. “Swift 进阶。” Apple Books. 

{ $1 } 来作为合并两个值的策略

Hashable要求

let settingsAsStrings = settings.mapValues { setting -> String in
switch setting {
case .text(let text): return text
case .int(let number): return String(number)
case .bool(let value): return String(value)
}
}
settingsAsStrings // ["Airplane Mode": "false", "Name": "Jane\'s iPhone"]
// Swift 4.1
struct Person: Hashable {
  var age: Int
  var name: String

  var hashValue: Int {
     return age.hashValue ^ name.hashValue &* 16777619
  }
}

首先 ^ 是异或,&* 是防止乘法溢出 crash 的运算符,16777619 显然也不是一个随便选择的数字

在Swift4.2中简化为

// Swift 4.2
struct Person: Hashable {
  var age: Int
  var name: String

func hash(into hasher: inout Hasher) {
  hasher.combine(age)
  hasher.combine(name)
  }
}

标准库的通用哈希函数使用一个随机种子作为其输入之一。也就是说,字符串 "abc" 的哈希值在每次程序执行时都会是不同的。随机种子是一种用来防止有针对性的哈希洪泛式拒绝服务攻击的安全措施。因为字典和集合是按照存储在哈希表中的顺序来迭代它们的元素,并且由于这个顺序是由哈希值决定的,所以这意味着相同的代码在每次执行时会产生不同的迭代顺序。如果你需要哈希值每次都一样,例如为了测试,那么可以通过设置环境变量SWIFT_DETERMINISTIC_HASHING=1 来禁用随机种子,但是你不应该在正式环境中这么做.
注:对于非值语义类型.例如可变对象作为字典的键时,需要特别小心,因为它的值改变后,就无法在字典中找到它。

参考文章

上一篇下一篇

猜你喜欢

热点阅读