Swift底層進階--020:Dictionary源碼解析

  • Swift字典用來存儲無序的相同類型數(shù)據(jù)的集合,字典會強制檢測元素的類型奕删,如果類型不同則會報錯。
  • Swift字典每個值(value)都關(guān)聯(lián)唯一的鍵(key),鍵作為字典中的這個值數(shù)據(jù)的標(biāo)識符孕暇。
  • 和數(shù)組中的元素不同,字典中的元素并沒有具體順序赤兴,需要通過key訪問到元素妖滔。
  • 字典的key沒有類型限制,可以是IntString桶良,但必須是唯一的座舍。
  • 如果創(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
  • 其中_scale2n次方數(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)用_HashTablescale方法资盅,計算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每庆,而bucketCount2n次方數(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)存空間,分別存儲KeyValue
Dictionary內(nèi)存布局
  • Dictionary包含DictionaryStorage
  • DictionaryStorage類包含metaData蓝晒、refCount腮出、_count_capacity
  • 中間64位存儲_scale芝薇、_reservedScale胚嘲、_extra_age
  • 接下來存儲的_seed剩燥、_rawKeys慢逾、_rawValuesmetaAddr
  • 其中_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

  • 0x00007fff99540800metaData元類型
  • 0x0000000000000002refCoutn引用計數(shù)
  • 0x0000000000000004_count
  • 0x0000000000000006_capacity
  • 0xcf1ba4a80000000364位連續(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)的是key2ASCII碼
  • 后面的0xe100000000000000表示key2的字符串長度
  • 同理后面的0x0000000000000033毫缆、0x0000000000000031唯竹、0x0000000000000034分別對應(yīng)的key值是31苦丁、4

通過x/16g查看_rawValues內(nèi)存地址

  • _rawKeys同理浸颓,前面的0x000000006b6e61480x00000069636f6f43旺拉、0x0000000079646f4b产上、0x0000000000746143表示value
  • 后面的0xe4000000000000000xe500000000000000蛾狗、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)值的枚舉類型
  • 通過_variantlookup方法谢鹊,找到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撇贺,找到bucketfound

找到find的定義:

  @inlinable
  @inline(__always)
  internal func find(_ key: Key) -> (bucket: Bucket, found: Bool) {
    return _storage.find(key)
  }

調(diào)用_storagefind方法赌莺,傳入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方法艘狭,傳入keyhashValue

找到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)用hashTableidealBucket方法,傳入hashValue翠订,返回bucket
  • 通過_isOccupied方法判斷bucket是否被占用

打開HashTable.swift文件巢音,找到idealBucket的定義:

  @inlinable
  @inline(__always)
  internal func idealBucket(forHashValue hashValue: Int) -> Bucket {
    return Bucket(offset: hashValue & bucketMask)
  }

hashValuebucketMask進行&運算,計算index保存到bucket

找到_isOccupied的定義:

  @inlinable
  @inline(__always)
  internal func _isOccupied(_ bucket: Bucket) -> Bool {
    _internalInvariant(isValid(bucket))
    return words[bucket.word].uncheckedContains(bucket.bit)
  }
  • 與原有的二進制位進行計算尽超,使用uncheckedContains返回01官撼,查看是否包含當(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找到bucketfound
  • 如果存在巩踏,使用_values加上bucket.offset秃诵,將value覆蓋
  • 如果不存在,使用_insert方法塞琼,在當(dāng)前bucket位置插入keyvalue

Dictionary插??個值的過程:

  • 通過key找到key. hashValue
  • 通過key. hashValuebucketMask進行&運算菠净,計算出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")
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末派近,一起剝皮案震驚了整個濱河市攀唯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渴丸,老刑警劉巖革答,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異曙强,居然都是意外死亡,警方通過查閱死者的電腦和手機途茫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門碟嘴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人囊卜,你說我怎么就攤上這事娜扇〈砦郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵雀瓢,是天一觀的道長枢析。 經(jīng)常有香客問我,道長刃麸,這世上最難降的妖魔是什么醒叁? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮泊业,結(jié)果婚禮上把沼,老公的妹妹穿的比我還像新娘。我一直安慰自己吁伺,他們只是感情好饮睬,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著篮奄,像睡著了一般捆愁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窟却,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天昼丑,我揣著相機與錄音,去河邊找鬼间校。 笑死矾克,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的憔足。 我是一名探鬼主播胁附,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼滓彰!你這毒婦竟也來了控妻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揭绑,失蹤者是張志新(化名)和其女友劉穎弓候,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體他匪,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡菇存,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了邦蜜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片依鸥。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖悼沈,靈堂內(nèi)的尸體忽然破棺而出贱迟,到底是詐尸還是另有隱情姐扮,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布衣吠,位于F島的核電站茶敏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缚俏。R本人自食惡果不足惜惊搏,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袍榆。 院中可真熱鬧胀屿,春花似錦、人聲如沸包雀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽才写。三九已至葡兑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赞草,已是汗流浹背讹堤。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留厨疙,地道東北人洲守。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像沾凄,于是被迫代替她去往敵國和親梗醇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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