Swift
字典用來存儲無序的相同類型數(shù)據(jù)的集合,字典會強制檢測元素的類型奕删,如果類型不同則會報錯。Swift
字典每個值(value
)都關(guān)聯(lián)唯一的鍵(key
),鍵作為字典中的這個值數(shù)據(jù)的標(biāo)識符孕暇。- 和數(shù)組中的元素不同,字典中的元素并沒有具體順序赤兴,需要通過
key
訪問到元素妖滔。- 字典的
key
沒有類型限制,可以是Int
或String
桶良,但必須是唯一的座舍。- 如果創(chuàng)建一個字典,并賦值給一個變量陨帆,則創(chuàng)建的字典是可以修改的曲秉。這意味著在創(chuàng)建字典后,可以通過添加疲牵、刪除承二、修改的方式改變字典里的元素。
- 如果將一個字典賦值給常量纲爸,字典是不可修改的亥鸠,并且字典的大小和內(nèi)容都不可以修改。
基本定義
Dictionary
類型的鍵值對必須遵循Hashable
協(xié)議识啦,因為它使用的就是哈希表
哈希表
哈希表(
Hash table
)也叫散列表负蚊,是根據(jù)關(guān)鍵字(Key value
)?直接訪問在內(nèi)存存儲位置的數(shù)據(jù)結(jié)構(gòu)。也就是說袁滥,它通過計算?個關(guān)于鍵值的函數(shù)盖桥,將所需查詢的數(shù)據(jù)映射到表中?個位置來訪問記錄,這加快了查找速度题翻。這個映射函數(shù)稱做散列函數(shù)揩徊,存放記錄的數(shù)組稱做散列表腰鬼。
散列函數(shù)
散列函數(shù)又稱散列算法、哈希函數(shù)塑荒。它的目標(biāo)是計算
key
在數(shù)組中的下標(biāo)熄赡。幾種常用的散列函數(shù)構(gòu)造方法:
- 直接尋址法
- 數(shù)字分析法
- 平?取中法
- 折疊法
- 隨機數(shù)法
- 除留余數(shù)法
哈希沖突
再優(yōu)秀的哈希算法永遠無法避免出現(xiàn)哈希沖突。哈希沖突指的是兩個不同的
key
經(jīng)過哈希計算后得到的數(shù)組下標(biāo)是相同的齿税。哈希沖突幾種常用的解決方法:
- 開放定址法
- 拉鏈法
負載因?
負載因子 = 填?表中的元素個數(shù) / 散列表的?度
一個非空散列表的
負載因子
是填?表中的元素個數(shù) / 散列表的?度
彼硫。這是面臨再次散列或擴容時的決策參數(shù),有助于確定散列函數(shù)的效率凌箕。也就是說拧篮,該參數(shù)指出了散列函數(shù)是否將關(guān)鍵字均勻分布。
內(nèi)存布局
Dictionary
是否是連續(xù)的內(nèi)存地址空間牵舱?如果不是串绩,當(dāng)前元素和元素之前的關(guān)系如何確定?如何計算?個元素的地址芜壁?
通過字?量方式創(chuàng)建?個字典:
var dic = ["1": "Kody", "2": "Hank", "3": "Cooci", "4" : "Cat"]
源碼分析
上述代碼礁凡,通過字?量方式創(chuàng)建
Dictionary
,在源碼中可以直接搜索literal
打開
Dictionary.swift
文件慧妄,找到init(dictionaryLiteral elements:)
的定義:
@inlinable
@_effects(readonly)
@_semantics("optimize.sil.specialize.generic.size.never")
public init(dictionaryLiteral elements: (Key, Value)...) {
let native = _NativeDictionary<Key, Value>(capacity: elements.count)
for (key, value) in elements {
let (bucket, found) = native.find(key)
_precondition(!found, "Dictionary literal contains duplicate keys")
native._insert(at: bucket, key: key, value: value)
}
self.init(_native: native)
}
- 創(chuàng)建了一個
_NativeDictionary
的實例對象- 遍歷
elements
顷牌,通過實例對象的find
方法,找到key
值對應(yīng)的bucket
bucket
相當(dāng)于要插入的位置- 將
key
塞淹、value
循環(huán)插入到bucket
中- 最后調(diào)用
init
方法found
:布爾類型窟蓝,如果字典初始化時,字面量里包含重復(fù)key
值饱普,運行時報錯Dictionary literal contains duplicate keys
打開
NativeDictionary.swift
文件疗锐,找到_NativeDictionary
的定義:
@usableFromInline
@frozen
internal struct _NativeDictionary<Key: Hashable, Value> {
@usableFromInline
internal typealias Element = (key: Key, value: Value)
/// See this comments on __RawDictionaryStorage and its subclasses to
/// understand why we store an untyped storage here.
@usableFromInline
internal var _storage: __RawDictionaryStorage
/// Constructs an instance from the empty singleton.
@inlinable
internal init() {
self._storage = __RawDictionaryStorage.empty
}
/// Constructs a dictionary adopting the given storage.
@inlinable
internal init(_ storage: __owned __RawDictionaryStorage) {
self._storage = storage
}
@inlinable
internal init(capacity: Int) {
if capacity == 0 {
self._storage = __RawDictionaryStorage.empty
} else {
self._storage = _DictionaryStorage<Key, Value>.allocate(capacity: capacity)
}
}
#if _runtime(_ObjC)
@inlinable
internal init(_ cocoa: __owned __CocoaDictionary) {
self.init(cocoa, capacity: cocoa.count)
}
@inlinable
internal init(_ cocoa: __owned __CocoaDictionary, capacity: Int) {
if capacity == 0 {
self._storage = __RawDictionaryStorage.empty
} else {
_internalInvariant(cocoa.count <= capacity)
self._storage =
_DictionaryStorage<Key, Value>.convert(cocoa, capacity: capacity)
for (key, value) in cocoa {
insertNew(
key: _forceBridgeFromObjectiveC(key, Key.self),
value: _forceBridgeFromObjectiveC(value, Value.self))
}
}
}
#endif
}
_NativeDictionary
是一個結(jié)構(gòu)體- 使用
init(capacity:)
方法初始化,如果capacity
容量等于0
费彼,使用__RawDictionaryStorage.empty
。否則使用_DictionaryStorage
口芍,通過allocate
方法箍铲,按容量大小分配內(nèi)存空間
打開
DictionaryStorage.swift
文件,找到_DictionaryStorage
的定義:
@usableFromInline
final internal class _DictionaryStorage<Key: Hashable, Value>
: __RawDictionaryStorage, _NSDictionaryCore {
// This type is made with allocWithTailElems, so no init is ever called.
// But we still need to have an init to satisfy the compiler.
@nonobjc
override internal init(_doNotCallMe: ()) {
_internalInvariantFailure("This class cannot be directly initialized")
}
deinit {
guard _count > 0 else { return }
if !_isPOD(Key.self) {
let keys = self._keys
for bucket in _hashTable {
(keys + bucket.offset).deinitialize(count: 1)
}
}
if !_isPOD(Value.self) {
let values = self._values
for bucket in _hashTable {
(values + bucket.offset).deinitialize(count: 1)
}
}
_count = 0
_fixLifetime(self)
}
@inlinable
final internal var _keys: UnsafeMutablePointer<Key> {
@inline(__always)
get {
return self._rawKeys.assumingMemoryBound(to: Key.self)
}
}
@inlinable
final internal var _values: UnsafeMutablePointer<Value> {
@inline(__always)
get {
return self._rawValues.assumingMemoryBound(to: Value.self)
}
}
internal var asNative: _NativeDictionary<Key, Value> {
return _NativeDictionary(self)
}
#if _runtime(_ObjC)
@objc
internal required init(
objects: UnsafePointer<AnyObject?>,
forKeys: UnsafeRawPointer,
count: Int
) {
_internalInvariantFailure("This class cannot be directly initialized")
}
@objc(copyWithZone:)
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
return self
}
@objc
internal var count: Int {
return _count
}
@objc(keyEnumerator)
internal func keyEnumerator() -> _NSEnumerator {
return _SwiftDictionaryNSEnumerator<Key, Value>(asNative)
}
@objc(countByEnumeratingWithState:objects:count:)
internal func countByEnumerating(
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
objects: UnsafeMutablePointer<AnyObject>?, count: Int
) -> Int {
defer { _fixLifetime(self) }
let hashTable = _hashTable
var theState = state.pointee
if theState.state == 0 {
theState.state = 1 // Arbitrary non-zero value.
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
theState.extra.0 = CUnsignedLong(hashTable.startBucket.offset)
}
// Test 'objects' rather than 'count' because (a) this is very rare anyway,
// and (b) the optimizer should then be able to optimize away the
// unwrapping check below.
if _slowPath(objects == nil) {
return 0
}
let unmanagedObjects = _UnmanagedAnyObjectArray(objects!)
var bucket = _HashTable.Bucket(offset: Int(theState.extra.0))
let endBucket = hashTable.endBucket
_precondition(bucket == endBucket || hashTable.isOccupied(bucket),
"Invalid fast enumeration state")
var stored = 0
for i in 0..<count {
if bucket == endBucket { break }
let key = _keys[bucket.offset]
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(key)
stored += 1
bucket = hashTable.occupiedBucket(after: bucket)
}
theState.extra.0 = CUnsignedLong(bucket.offset)
state.pointee = theState
return stored
}
@objc(objectForKey:)
internal func object(forKey aKey: AnyObject) -> AnyObject? {
guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self)
else { return nil }
let (bucket, found) = asNative.find(nativeKey)
guard found else { return nil }
let value = asNative.uncheckedValue(at: bucket)
return _bridgeAnythingToObjectiveC(value)
}
@objc(getObjects:andKeys:count:)
internal func getObjects(
_ objects: UnsafeMutablePointer<AnyObject>?,
andKeys keys: UnsafeMutablePointer<AnyObject>?,
count: Int) {
_precondition(count >= 0, "Invalid count")
guard count > 0 else { return }
var i = 0 // Current position in the output buffers
switch (_UnmanagedAnyObjectArray(keys), _UnmanagedAnyObjectArray(objects)) {
case (let unmanagedKeys?, let unmanagedObjects?):
for (key, value) in asNative {
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
i += 1
guard i < count else { break }
}
case (let unmanagedKeys?, nil):
for (key, _) in asNative {
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
i += 1
guard i < count else { break }
}
case (nil, let unmanagedObjects?):
for (_, value) in asNative {
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
i += 1
guard i < count else { break }
}
case (nil, nil):
// Do nothing.
break
}
}
#endif
}
_DictionaryStorage
是一個類鬓椭,繼承自__RawDictionaryStorage
颠猴,遵循_NSDictionaryCore
協(xié)議_DictionaryStorage
使用final
關(guān)鍵字修飾,子類不可重寫
找到
__RawDictionaryStorage
的定義:
@_fixed_layout
@usableFromInline
@_objc_non_lazy_realization
internal class __RawDictionaryStorage: __SwiftNativeNSDictionary {
// NOTE: The precise layout of this type is relied on in the runtime to
// provide a statically allocated empty singleton. See
// stdlib/public/stubs/GlobalObjects.cpp for details.
/// The current number of occupied entries in this dictionary.
@usableFromInline
@nonobjc
internal final var _count: Int
/// The maximum number of elements that can be inserted into this set without
/// exceeding the hash table's maximum load factor.
@usableFromInline
@nonobjc
internal final var _capacity: Int
/// The scale of this dictionary. The number of buckets is 2 raised to the
/// power of `scale`.
@usableFromInline
@nonobjc
internal final var _scale: Int8
/// The scale corresponding to the highest `reserveCapacity(_:)` call so far,
/// or 0 if there were none. This may be used later to allow removals to
/// resize storage.
///
/// FIXME: <rdar://problem/18114559> Shrink storage on deletion
@usableFromInline
@nonobjc
internal final var _reservedScale: Int8
// Currently unused, set to zero.
@nonobjc
internal final var _extra: Int16
/// A mutation count, enabling stricter index validation.
@usableFromInline
@nonobjc
internal final var _age: Int32
/// The hash seed used to hash elements in this dictionary instance.
@usableFromInline
internal final var _seed: Int
/// A raw pointer to the start of the tail-allocated hash buffer holding keys.
@usableFromInline
@nonobjc
internal final var _rawKeys: UnsafeMutableRawPointer
/// A raw pointer to the start of the tail-allocated hash buffer holding
/// values.
@usableFromInline
@nonobjc
internal final var _rawValues: UnsafeMutableRawPointer
// This type is made with allocWithTailElems, so no init is ever called.
// But we still need to have an init to satisfy the compiler.
@nonobjc
internal init(_doNotCallMe: ()) {
_internalInvariantFailure("This class cannot be directly initialized")
}
@inlinable
@nonobjc
internal final var _bucketCount: Int {
@inline(__always) get { return 1 &<< _scale }
}
@inlinable
@nonobjc
internal final var _metadata: UnsafeMutablePointer<_HashTable.Word> {
@inline(__always) get {
let address = Builtin.projectTailElems(self, _HashTable.Word.self)
return UnsafeMutablePointer(address)
}
}
// The _HashTable struct contains pointers into tail-allocated storage, so
// this is unsafe and needs `_fixLifetime` calls in the caller.
@inlinable
@nonobjc
internal final var _hashTable: _HashTable {
@inline(__always) get {
return _HashTable(words: _metadata, bucketCount: _bucketCount)
}
}
}
__RawDictionaryStorage
類小染,定義基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)翘瓮,例如_count
、_capacity
- 其中
_scale
為2
的n
次方數(shù)裤翩,參與計算_bucketCount
_seed
為哈希種子
找到
allocate
的定義:
@usableFromInline
@_effects(releasenone)
static internal func allocate(capacity: Int) -> _DictionaryStorage {
let scale = _HashTable.scale(forCapacity: capacity)
return allocate(scale: scale, age: nil, seed: nil)
}
- 調(diào)用
_HashTable
的scale
方法资盅,計算scale
- 調(diào)用
allocate
方法,傳入scale
打開
HashTable.swift
文件,找到_HashTable
的定義:
@usableFromInline
@frozen
internal struct _HashTable {
@usableFromInline
internal typealias Word = _UnsafeBitset.Word
@usableFromInline
internal var words: UnsafeMutablePointer<Word>
@usableFromInline
internal let bucketMask: Int
@inlinable
@inline(__always)
internal init(words: UnsafeMutablePointer<Word>, bucketCount: Int) {
_internalInvariant(bucketCount > 0 && bucketCount & (bucketCount - 1) == 0,
"bucketCount must be a power of two")
self.words = words
// The bucket count is a power of two, so subtracting 1 will never overflow
// and get us a nice mask.
self.bucketMask = bucketCount &- 1
}
@inlinable
internal var bucketCount: Int {
@inline(__always) get {
return bucketMask &+ 1
}
}
@inlinable
internal var wordCount: Int {
@inline(__always) get {
return _UnsafeBitset.wordCount(forCapacity: bucketCount)
}
}
}
words
可以理解為二進制位呵扛,記錄當(dāng)前位置是否插入元素bucketMask
等于bucketCount-1
每庆,而bucketCount
是2
的n
次方數(shù),所以bucketMask
相當(dāng)于掩碼_HashTable
并不直接存儲數(shù)據(jù)今穿,而是存儲二進制位
回到
DictionaryStorage.swift
文件缤灵,找到allocate
的定義:
static internal func allocate(
scale: Int8,
age: Int32?,
seed: Int?
) -> _DictionaryStorage {
// The entry count must be representable by an Int value; hence the scale's
// peculiar upper bound.
_internalInvariant(scale >= 0 && scale < Int.bitWidth - 1)
let bucketCount = (1 as Int) &<< scale
let wordCount = _UnsafeBitset.wordCount(forCapacity: bucketCount)
let storage = Builtin.allocWithTailElems_3(
_DictionaryStorage<Key, Value>.self,
wordCount._builtinWordValue, _HashTable.Word.self,
bucketCount._builtinWordValue, Key.self,
bucketCount._builtinWordValue, Value.self)
let metadataAddr = Builtin.projectTailElems(storage, _HashTable.Word.self)
let keysAddr = Builtin.getTailAddr_Word(
metadataAddr, wordCount._builtinWordValue, _HashTable.Word.self,
Key.self)
let valuesAddr = Builtin.getTailAddr_Word(
keysAddr, bucketCount._builtinWordValue, Key.self,
Value.self)
storage._count = 0
storage._capacity = _HashTable.capacity(forScale: scale)
storage._scale = scale
storage._reservedScale = 0
storage._extra = 0
if let age = age {
storage._age = age
} else {
// The default mutation count is simply a scrambled version of the storage
// address.
storage._age = Int32(
truncatingIfNeeded: ObjectIdentifier(storage).hashValue)
}
storage._seed = seed ?? _HashTable.hashSeed(for: storage, scale: scale)
storage._rawKeys = UnsafeMutableRawPointer(keysAddr)
storage._rawValues = UnsafeMutableRawPointer(valuesAddr)
// Initialize hash table metadata.
storage._hashTable.clear()
return storage
}
- 通過
&
運算符計算bucketCount
wordCount
通過bucketCount
計算要記錄的二進制位allocWithTailElems_3
在類的后面分配連續(xù)的內(nèi)存空間,分別存儲Key
和Value
Dictionary內(nèi)存布局
Dictionary
包含DictionaryStorage
類DictionaryStorage
類包含metaData
蓝晒、refCount
腮出、_count
、_capacity
- 中間
64位
存儲_scale
芝薇、_reservedScale
胚嘲、_extra
、_age
- 接下來存儲的
_seed
剩燥、_rawKeys
慢逾、_rawValues
、metaAddr
- 其中
_rawKeys
灭红、_rawValues
存儲的是數(shù)組地址
通過
LLDB
調(diào)試來驗證?下:通過字?量方式創(chuàng)建?個字典:
var dic = ["1": "Kody", "2": "Hank", "3": "Cooci", "4" : "Cat"]
通過
withUnsafePointer
打印dic
內(nèi)存地址
通過
x/8g
查看dic
的內(nèi)存地址侣滩,里面包含了一個堆上的地址0x000000010067aed0
通過
x/8g
查看內(nèi)存地址0x000000010067aed0
0x00007fff99540800
:metaData
元類型0x0000000000000002
:refCoutn
引用計數(shù)0x0000000000000004
:_count
0x0000000000000006
:_capacity
0xcf1ba4a800000003
:64位
連續(xù)內(nèi)存,包含_scale
变擒、_reservedScale
君珠、_extra
、_age
0x000000010067aed0
:_seed
種子娇斑,以對象創(chuàng)建的地址作為隨機數(shù)的開始0x000000010067af18
:_rawKeys
0x000000010067af98
:_rawValues
通過
x/16g
查看_rawKeys
內(nèi)存地址
- 連續(xù)內(nèi)存中并不是連續(xù)存儲的策添,
0x0000000000000032
轉(zhuǎn)為十進制
是50
,對應(yīng)的是key
值2
的ASCII碼
- 后面的
0xe100000000000000
表示key
值2
的字符串長度- 同理后面的
0x0000000000000033
毫缆、0x0000000000000031
唯竹、0x0000000000000034
分別對應(yīng)的key
值是3
、1
苦丁、4
通過
x/16g
查看_rawValues
內(nèi)存地址
- 和
_rawKeys
同理浸颓,前面的0x000000006b6e6148
、0x00000069636f6f43
旺拉、0x0000000079646f4b
产上、0x0000000000746143
表示value
值- 后面的
0xe400000000000000
、0xe500000000000000
蛾狗、0xe400000000000000
晋涣、0xe300000000000000
表示value
值的字符串長度
Dictionary插??個值
var dic = ["1": "Kody", "2": "Hank", "3": "Cooci", "4" : "Cat"]
dic["5"] = "Swift"
打開
Dictionary.swift
文件,找到subscript
的定義:
@inlinable
public subscript(
key: Key, default defaultValue: @autoclosure () -> Value
) -> Value {
@inline(__always)
get {
return _variant.lookup(key) ?? defaultValue()
}
@inline(__always)
_modify {
let (bucket, found) = _variant.mutatingFind(key)
let native = _variant.asNative
if !found {
let value = defaultValue()
native._insert(at: bucket, key: key, value: value)
}
let address = native._values + bucket.offset
defer { _fixLifetime(self) }
yield &address.pointee
}
}
get
方法里沉桌,_variant
是一個關(guān)聯(lián)值的枚舉類型- 通過
_variant
的lookup
方法谢鹊,找到key
值是否存在
打開
NativeDictionary.swift
文件算吩,找到lookup
的定義:
@inlinable
@inline(__always)
func lookup(_ key: Key) -> Value? {
if count == 0 {
// Fast path that avoids computing the hash of the key.
return nil
}
let (bucket, found) = self.find(key)
guard found else { return nil }
return self.uncheckedValue(at: bucket)
}
- 通過
find
方法,傳入key
撇贺,找到bucket
和found
找到
find
的定義:
@inlinable
@inline(__always)
internal func find(_ key: Key) -> (bucket: Bucket, found: Bool) {
return _storage.find(key)
}
調(diào)用
_storage
的find
方法赌莺,傳入key
值
打開
DictionaryStorage.swift
文件,找到find
的定義:
@_alwaysEmitIntoClient
@inline(never)
internal final func find<Key: Hashable>(_ key: Key) -> (bucket: _HashTable.Bucket, found: Bool) {
return find(key, hashValue: key._rawHashValue(seed: _seed))
}
- 調(diào)用
key
的_rawHashValue
方法松嘶,傳入_seed
種子- 本質(zhì)上就是通過
key
值得到hashValue
- 調(diào)用
find
方法艘狭,傳入key
和hashValue
找到
find(_ key:, hashValue:)
的定義:
@_alwaysEmitIntoClient
@inline(never)
internal final func find<Key: Hashable>(_ key: Key, hashValue: Int) -> (bucket: _HashTable.Bucket, found: Bool) {
let hashTable = _hashTable
var bucket = hashTable.idealBucket(forHashValue: hashValue)
while hashTable._isOccupied(bucket) {
if uncheckedKey(at: bucket) == key {
return (bucket, true)
}
bucket = hashTable.bucket(wrappedAfter: bucket)
}
return (bucket, false)
}
}
- 調(diào)用
hashTable
的idealBucket
方法,傳入hashValue
翠订,返回bucket
- 通過
_isOccupied
方法判斷bucket
是否被占用
打開
HashTable.swift
文件巢音,找到idealBucket
的定義:
@inlinable
@inline(__always)
internal func idealBucket(forHashValue hashValue: Int) -> Bucket {
return Bucket(offset: hashValue & bucketMask)
}
hashValue
和bucketMask
進行&
運算,計算index
保存到bucket
中
找到
_isOccupied
的定義:
@inlinable
@inline(__always)
internal func _isOccupied(_ bucket: Bucket) -> Bool {
_internalInvariant(isValid(bucket))
return words[bucket.word].uncheckedContains(bucket.bit)
}
- 與原有的二進制位進行計算尽超,使用
uncheckedContains
返回0
或1
官撼,查看是否包含當(dāng)前bucket
找到
bucket(wrappedAfter bucket:)
的定義:
@inlinable
@inline(__always)
internal func bucket(wrappedAfter bucket: Bucket) -> Bucket {
// The bucket is less than bucketCount, which is power of two less than
// Int.max. Therefore adding 1 does not overflow.
return Bucket(offset: (bucket.offset &+ 1) & bucketMask)
}
bucket.offset
進行+1
操作,再和bucketMask
進行&
運算似谁,然后重新構(gòu)建bucket
打開
NativeDictionary.swift
文件傲绣,找到uncheckedValue
的定義:
@inlinable
@inline(__always)
internal func uncheckedValue(at bucket: Bucket) -> Value {
defer { _fixLifetime(self) }
_internalInvariant(hashTable.isOccupied(bucket))
return _values[bucket.offset]
}
_values
是連續(xù)存儲的數(shù)組,直接通過bucket.offset
作為index
獲取指定位置的元素
找到
setValue
的定義:
@inlinable
internal mutating func setValue(
_ value: __owned Value,
forKey key: Key,
isUnique: Bool
) {
let (bucket, found) = mutatingFind(key, isUnique: isUnique)
if found {
(_values + bucket.offset).pointee = value
} else {
_insert(at: bucket, key: key, value: value)
}
}
- 通過
mutatingFind
找到bucket
和found
- 如果存在巩踏,使用
_values
加上bucket.offset
秃诵,將value
覆蓋- 如果不存在,使用
_insert
方法塞琼,在當(dāng)前bucket
位置插入key
和value
Dictionary
插??個值的過程:
- 通過
key
找到key. hashValue
- 通過
key. hashValue
和bucketMask
進行&
運算菠净,計算出index
- 通過
index
查找是否有對應(yīng)key
值,如果有采用開放定址法查找下一個bucket
是否為空彪杉,如果為空返回對應(yīng)元素- 對于
setValue
也是一樣毅往,通過key
值先查找,再賦值
使用Swift代碼實現(xiàn)簡單的HashTable
struct HashTable<Key: Hashable, Value> {
typealias Element = (key: Key, value: Value)
typealias Bucket = [Element]
var buckets: [Bucket]
var count = 0
init(capacity: Int){
buckets = Array<Bucket>(repeating: [Element](), count: capacity)
}
func index(key: Key) -> Int {
return abs(key.hashValue) % buckets.count
}
subscript(key: Key) -> Value? {
get {
return getValue(key: key)
}
set {
if let value = newValue {
updateValue(value, key)
}
else{
removeValue(key)
}
}
}
func getValue(key: Key) -> Value? {
let index = self.index(key: key)
for element in buckets[index] {
if(element.key == key){
return element.value
}
}
return nil
}
mutating func updateValue(_ value: Value, _ key: Key) -> Value? {
let index = self.index(key: key)
for (i, element) in buckets[index].enumerated() {
if(element.key == key){
let originValue = element.value
buckets[index][i].value = value
return originValue
}
}
buckets[index].append((key: key, value: value))
count += 1
return nil
}
mutating func removeValue(_ key: Key) {
let index = self.index(key: key)
for (i, element) in buckets[index].enumerated() {
if(element.key == key){
buckets[index].remove(at: i)
count -= 1
}
}
}
}
var ht = HashTable<String, String>(capacity: 5)
ht["1"] = "Kody"
print(ht["1"])
//輸出以下內(nèi)容:
//Optional("Kody")