[Swift]实现Swift的两个协议:Hashable协议和Equatable协议(Type does not conform to protocol 'Hashable')
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/ )
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/10815581.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
【Command 】+【鼠标右键】-> 【Jump to Definition】
Hashable协议:
1 public protocol Hashable : Equatable { 2 3 /// The hash value. 4 /// 5 /// Hash values are not guaranteed to be equal across different executions of 6 /// your program. Do not save hash values to use during a future execution. 7 /// 8 /// - Important: `hashValue` is deprecated as a `Hashable` requirement. To 9 /// conform to `Hashable`, implement the `hash(into:)` requirement instead. 10 var hashValue: Int { get } 11 12 /// Hashes the essential components of this value by feeding them into the 13 /// given hasher. 14 /// 15 /// Implement this method to conform to the `Hashable` protocol. The 16 /// components used for hashing must be the same as the components compared 17 /// in your type's `==` operator implementation. Call `hasher.combine(_:)` 18 /// with each of these components. 19 /// 20 /// - Important: Never call `finalize()` on `hasher`. Doing so may become a 21 /// compile-time error in the future. 22 /// 23 /// - Parameter hasher: The hasher to use when combining the components 24 /// of this instance. 25 func hash(into hasher: inout Hasher) 26 }
翻译版本:
1 public protocol Hashable : Equatable { 2 ///散列值。 3 // 4 ///不能保证哈希值在不同的 5 ///你的程序。不要保存哈希值以在将来执行时使用。 6 // 7 ///-重要提示:“hashvalue”被弃用为“hashable”要求。到 8 ///符合“hashable”,改为实现“hash(into:)”要求。 9 10 var hashValue: Int { get } 11 12 ///通过将此值的基本组件放入 13 ///给定哈希。 14 // 15 ///实现此方法以符合“hashable”协议。这个 16 ///用于散列的组件必须与比较的组件相同 17 ///在类型的`=`运算符实现中。调用'hasher.combine(:)` 18 ///每个组件。 19 // 20 ///-重要提示:不要在“hasher”上调用“finalize()”。这样做可能会成为 21 ///将来出现编译时错误。 22 // 23 ///-参数散列器:组合组件时要使用的散列器 24 ///此实例的。 25 26 func hash(into hasher: inout Hasher) 27 }
Equatable 协议:
1 /// A type-erased hashable value. 2 3 /// 4 5 /// The `AnyHashable` type forwards equality comparisons and hashing operations 6 7 /// to an underlying hashable value, hiding its specific underlying type. 8 9 /// 10 11 /// You can store mixed-type keys in dictionaries and other collections that 12 13 /// require `Hashable` conformance by wrapping mixed-type keys in 14 15 /// `AnyHashable` instances: 16 17 /// 18 19 /// let descriptions: [AnyHashable: Any] = [ 20 21 /// AnyHashable("😄"): "emoji", 22 23 /// AnyHashable(42): "an Int", 24 25 /// AnyHashable(Int8(43)): "an Int8", 26 27 /// AnyHashable(Set(["a", "b"])): "a set of strings" 28 29 /// ] 30 31 /// print(descriptions[AnyHashable(42)]!) // prints "an Int" 32 33 /// print(descriptions[AnyHashable(43)]) // prints "nil" 34 35 /// print(descriptions[AnyHashable(Int8(43))]!) // prints "an Int8" 36 37 /// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings" 38 39 public struct AnyHashable { 40 41 42 43 /// Creates a type-erased hashable value that wraps the given instance. 44 45 /// 46 47 /// The following example creates two type-erased hashable values: `x` wraps 48 49 /// an `Int` with the value 42, while `y` wraps a `UInt8` with the same 50 51 /// numeric value. Because the underlying types of `x` and `y` are 52 53 /// different, the two variables do not compare as equal despite having 54 55 /// equal underlying values. 56 57 /// 58 59 /// let x = AnyHashable(Int(42)) 60 61 /// let y = AnyHashable(UInt8(42)) 62 63 /// 64 65 /// print(x == y) 66 67 /// // Prints "false" because `Int` and `UInt8` are different types 68 69 /// 70 71 /// print(x == AnyHashable(Int(42))) 72 73 /// // Prints "true" 74 75 /// 76 77 /// - Parameter base: A hashable value to wrap. 78 79 public init<H>(_ base: H) where H : Hashable 80 81 82 83 /// The value wrapped by this instance. 84 85 /// 86 87 /// The `base` property can be cast back to its original type using one of 88 89 /// the casting operators (`as?`, `as!`, or `as`). 90 91 /// 92 93 /// let anyMessage = AnyHashable("Hello world!") 94 95 /// if let unwrappedMessage = anyMessage.base as? String { 96 97 /// print(unwrappedMessage) 98 99 /// } 100 101 /// // Prints "Hello world!" 102 103 public var base: Any { get } 104 105 106 107 public static func != (lhs: AnyHashable, rhs: AnyHashable) -> Bool 108 109 } 110 111 112 113 extension AnyHashable : Equatable { 114 115 116 117 /// Returns a Boolean value indicating whether two type-erased hashable 118 119 /// instances wrap the same type and value. 120 121 /// 122 123 /// Two instances of `AnyHashable` compare as equal if and only if the 124 125 /// underlying types have the same conformance to the `Equatable` protocol 126 127 /// and the underlying values compare as equal. 128 129 /// 130 131 /// The following example creates two type-erased hashable values: `x` wraps 132 133 /// an `Int` with the value 42, while `y` wraps a `UInt8` with the same 134 135 /// numeric value. Because the underlying types of `x` and `y` are 136 137 /// different, the two variables do not compare as equal despite having 138 139 /// equal underlying values. 140 141 /// 142 143 /// let x = AnyHashable(Int(42)) 144 145 /// let y = AnyHashable(UInt8(42)) 146 147 /// 148 149 /// print(x == y) 150 151 /// // Prints "false" because `Int` and `UInt8` are different types 152 153 /// 154 155 /// print(x == AnyHashable(Int(42))) 156 157 /// // Prints "true" 158 159 /// 160 161 /// - Parameters: 162 163 /// - lhs: A type-erased hashable value. 164 165 /// - rhs: Another type-erased hashable value. 166 167 public static func == (lhs: AnyHashable, rhs: AnyHashable) -> Bool 168 169 } 170 171 172 173 extension AnyHashable : Hashable { 174 175 176 177 /// The hash value. 178 179 public var hashValue: Int { get } 180 181 182 183 /// Hashes the essential components of this value by feeding them into the 184 185 /// given hasher. 186 187 /// 188 189 /// - Parameter hasher: The hasher to use when combining the components 190 191 /// of this instance. 192 193 public func hash(into hasher: inout Hasher) 194 195 } 196 197 198 199 extension AnyHashable : CustomStringConvertible { 200 201 202 203 /// A textual representation of this instance. 204 205 /// 206 207 /// Calling this property directly is discouraged. Instead, convert an 208 209 /// instance of any type to a string by using the `String(describing:)` 210 211 /// initializer. This initializer works with any type, and uses the custom 212 213 /// `description` property for types that conform to 214 215 /// `CustomStringConvertible`: 216 217 /// 218 219 /// struct Point: CustomStringConvertible { 220 221 /// let x: Int, y: Int 222 223 /// 224 225 /// var description: String { 226 227 /// return "(\(x), \(y))" 228 229 /// } 230 231 /// } 232 233 /// 234 235 /// let p = Point(x: 21, y: 30) 236 237 /// let s = String(describing: p) 238 239 /// print(s) 240 241 /// // Prints "(21, 30)" 242 243 /// 244 245 /// The conversion of `p` to a string in the assignment to `s` uses the 246 247 /// `Point` type's `description` property. 248 249 public var description: String { get } 250 251 } 252 253 254 255 extension AnyHashable : CustomDebugStringConvertible { 256 257 258 259 /// A textual representation of this instance, suitable for debugging. 260 261 /// 262 263 /// Calling this property directly is discouraged. Instead, convert an 264 265 /// instance of any type to a string by using the `String(reflecting:)` 266 267 /// initializer. This initializer works with any type, and uses the custom 268 269 /// `debugDescription` property for types that conform to 270 271 /// `CustomDebugStringConvertible`: 272 273 /// 274 275 /// struct Point: CustomDebugStringConvertible { 276 277 /// let x: Int, y: Int 278 279 /// 280 281 /// var debugDescription: String { 282 283 /// return "(\(x), \(y))" 284 285 /// } 286 287 /// } 288 289 /// 290 291 /// let p = Point(x: 21, y: 30) 292 293 /// let s = String(reflecting: p) 294 295 /// print(s) 296 297 /// // Prints "(21, 30)" 298 299 /// 300 301 /// The conversion of `p` to a string in the assignment to `s` uses the 302 303 /// `Point` type's `debugDescription` property. 304 305 public var debugDescription: String { get } 306 307 } 308 309 310 311 extension AnyHashable : CustomReflectable { 312 313 314 315 /// The custom mirror for this instance. 316 317 /// 318 319 /// If this type has value semantics, the mirror should be unaffected by 320 321 /// subsequent mutations of the instance. 322 323 public var customMirror: Mirror { get } 324 325 } 326 327 328 329 /// A type that can be hashed into a `Hasher` to produce an integer hash value. 330 331 /// 332 333 /// You can use any type that conforms to the `Hashable` protocol in a set or as 334 335 /// a dictionary key. Many types in the standard library conform to `Hashable`: 336 337 /// Strings, integers, floating-point and Boolean values, and even sets are 338 339 /// hashable by default. Some other types, such as optionals, arrays and ranges 340 341 /// automatically become hashable when their type arguments implement the same. 342 343 /// 344 345 /// Your own custom types can be hashable as well. When you define an 346 347 /// enumeration without associated values, it gains `Hashable` conformance 348 349 /// automatically, and you can add `Hashable` conformance to your other custom 350 351 /// types by implementing the `hash(into:)` method. For structs whose stored 352 353 /// properties are all `Hashable`, and for enum types that have all-`Hashable` 354 355 /// associated values, the compiler is able to provide an implementation of 356 357 /// `hash(into:)` automatically. 358 359 /// 360 361 /// Hashing a value means feeding its essential components into a hash function, 362 363 /// represented by the `Hasher` type. Essential components are those that 364 365 /// contribute to the type's implementation of `Equatable`. Two instances that 366 367 /// are equal must feed the same values to `Hasher` in `hash(into:)`, in the 368 369 /// same order. 370 371 /// 372 373 /// Conforming to the Hashable Protocol 374 375 /// =================================== 376 377 /// 378 379 /// To use your own custom type in a set or as the key type of a dictionary, 380 381 /// add `Hashable` conformance to your type. The `Hashable` protocol inherits 382 383 /// from the `Equatable` protocol, so you must also satisfy that protocol's 384 385 /// requirements. 386 387 /// 388 389 /// The compiler automatically synthesizes your custom type's `Hashable` and 390 391 /// requirements when you declare `Hashable` conformance in the type's original 392 393 /// declaration and your type meets these criteria: 394 395 /// 396 397 /// - For a `struct`, all its stored properties must conform to `Hashable`. 398 399 /// - For an `enum`, all its associated values must conform to `Hashable`. (An 400 401 /// `enum` without associated values has `Hashable` conformance even without 402 403 /// the declaration.) 404 405 /// 406 407 /// To customize your type's `Hashable` conformance, to adopt `Hashable` in a 408 409 /// type that doesn't meet the criteria listed above, or to extend an existing 410 411 /// type to conform to `Hashable`, implement the `hash(into:)` method in your 412 413 /// custom type. 414 415 /// 416 417 /// In your `hash(into:)` implementation, call `combine(_:)` on the provided 418 419 /// `Hasher` instance with the essential components of your type. To ensure 420 421 /// that your type meets the semantic requirements of the `Hashable` and 422 423 /// `Equatable` protocols, it's a good idea to also customize your type's 424 425 /// `Equatable` conformance to match. 426 427 /// 428 429 /// As an example, consider a `GridPoint` type that describes a location in a 430 431 /// grid of buttons. Here's the initial declaration of the `GridPoint` type: 432 433 /// 434 435 /// /// A point in an x-y coordinate system. 436 437 /// struct GridPoint { 438 439 /// var x: Int 440 441 /// var y: Int 442 443 /// } 444 445 /// 446 447 /// You'd like to create a set of the grid points where a user has already 448 449 /// tapped. Because the `GridPoint` type is not hashable yet, it can't be used 450 451 /// in a set. To add `Hashable` conformance, provide an `==` operator function 452 453 /// and implement the `hash(into:)` method. 454 455 /// 456 457 /// extension GridPoint: Hashable { 458 459 /// static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { 460 461 /// return lhs.x == rhs.x && lhs.y == rhs.y 462 463 /// } 464 465 /// 466 467 /// func hash(into hasher: inout Hasher) { 468 469 /// hasher.combine(x) 470 471 /// hasher.combine(y) 472 473 /// } 474 475 /// } 476 477 /// 478 479 /// The `hash(into:)` method in this example feeds the grid point's `x` and `y` 480 481 /// properties into the provided hasher. These properties are the same ones 482 483 /// used to test for equality in the `==` operator function. 484 485 /// 486 487 /// Now that `GridPoint` conforms to the `Hashable` protocol, you can create a 488 489 /// set of previously tapped grid points. 490 491 /// 492 493 /// var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)] 494 495 /// let nextTap = GridPoint(x: 0, y: 1) 496 497 /// if tappedPoints.contains(nextTap) { 498 499 /// print("Already tapped at (\(nextTap.x), \(nextTap.y)).") 500 501 /// } else { 502 503 /// tappedPoints.insert(nextTap) 504 505 /// print("New tap detected at (\(nextTap.x), \(nextTap.y)).") 506 507 /// } 508 509 /// // Prints "New tap detected at (0, 1).") 510 511 public protocol Hashable : Equatable { 512 513 514 515 /// The hash value. 516 517 /// 518 519 /// Hash values are not guaranteed to be equal across different executions of 520 521 /// your program. Do not save hash values to use during a future execution. 522 523 /// 524 525 /// - Important: `hashValue` is deprecated as a `Hashable` requirement. To 526 527 /// conform to `Hashable`, implement the `hash(into:)` requirement instead. 528 529 var hashValue: Int { get } 530 531 532 533 /// Hashes the essential components of this value by feeding them into the 534 535 /// given hasher. 536 537 /// 538 539 /// Implement this method to conform to the `Hashable` protocol. The 540 541 /// components used for hashing must be the same as the components compared 542 543 /// in your type's `==` operator implementation. Call `hasher.combine(_:)` 544 545 /// with each of these components. 546 547 /// 548 549 /// - Important: Never call `finalize()` on `hasher`. Doing so may become a 550 551 /// compile-time error in the future. 552 553 /// 554 555 /// - Parameter hasher: The hasher to use when combining the components 556 557 /// of this instance. 558 559 func hash(into hasher: inout Hasher) 560 561 } 562 563 564 565 /// The universal hash function used by `Set` and `Dictionary`. 566 567 /// 568 569 /// `Hasher` can be used to map an arbitrary sequence of bytes to an integer 570 571 /// hash value. You can feed data to the hasher using a series of calls to 572 573 /// mutating `combine` methods. When you've finished feeding the hasher, the 574 575 /// hash value can be retrieved by calling `finalize()`: 576 577 /// 578 579 /// var hasher = Hasher() 580 581 /// hasher.combine(23) 582 583 /// hasher.combine("Hello") 584 585 /// let hashValue = hasher.finalize() 586 587 /// 588 589 /// Within the execution of a Swift program, `Hasher` guarantees that finalizing 590 591 /// it will always produce the same hash value as long as it is fed the exact 592 593 /// same sequence of bytes. However, the underlying hash algorithm is designed 594 595 /// to exhibit avalanche effects: slight changes to the seed or the input byte 596 597 /// sequence will typically produce drastic changes in the generated hash value. 598 599 /// 600 601 /// - Note: Do not save or otherwise reuse hash values across executions of your 602 603 /// program. `Hasher` is usually randomly seeded, which means it will return 604 605 /// different values on every new execution of your program. The hash 606 607 /// algorithm implemented by `Hasher` may itself change between any two 608 609 /// versions of the standard library. 610 611 public struct Hasher { 612 613 614 615 /// Creates a new hasher. 616 617 /// 618 619 /// The hasher uses a per-execution seed value that is set during process 620 621 /// startup, usually from a high-quality random source. 622 623 public init() 624 625 626 627 /// Adds the given value to this hasher, mixing its essential parts into the 628 629 /// hasher state. 630 631 /// 632 633 /// - Parameter value: A value to add to the hasher. 634 635 @inlinable public mutating func combine<H>(_ value: H) where H : Hashable 636 637 638 639 /// Adds the contents of the given buffer to this hasher, mixing it into the 640 641 /// hasher state. 642 643 /// 644 645 /// - Parameter bytes: A raw memory buffer. 646 647 public mutating func combine(bytes: UnsafeRawBufferPointer) 648 649 650 651 /// Finalizes the hasher state and returns the hash value. 652 653 /// 654 655 /// Finalizing consumes the hasher: it is illegal to finalize a hasher you 656 657 /// don't own, or to perform operations on a finalized hasher. (These may 658 659 /// become compile-time errors in the future.) 660 661 /// 662 663 /// Hash values are not guaranteed to be equal across different executions of 664 665 /// your program. Do not save hash values to use during a future execution. 666 667 /// 668 669 /// - Returns: The hash value calculated by the hasher. 670 671 public __consuming func finalize() -> Int 672 673 }
【Command 】+【鼠标右键】-> 【Show Quick Help】
可以散列到散列器中以生成整数散列值的类型。
protocol Hashable : Equatable
您可以在集合中使用任何符合哈希协议的类型,也可以将其用作字典键。标准库中的许多类型都符合hashable:字符串、整数、浮点值和布尔值,默认情况下,偶数集都是可以hashable的。当一些其他类型(如选项、数组和范围)的类型参数实现相同时,它们会自动变为可哈希类型。
您自己的自定义类型也可以是哈希类型。当您定义一个没有关联值的枚举时,它会自动获得哈希一致性,并且您可以通过实现hash(into:)方法将哈希一致性添加到其他自定义类型中。对于其存储属性都是可哈希的结构,以及对于具有所有可哈希关联值的枚举类型,编译器能够自动提供哈希(into:)的实现。
散列值意味着将其基本组件输入散列函数,由散列类型表示。基本组成部分是那些有助于类型的实现等价的部分。两个相等的实例必须以相同的顺序向哈希(in to:)中的哈希器提供相同的值。
符合哈希协议
要在集合中使用您自己的自定义类型或将其用作字典的键类型,请将哈希一致性添加到您的类型中。哈希协议继承自等价协议,因此您还必须满足该协议的要求。
当您在类型的原始声明中声明可哈希一致性并且您的类型满足以下条件时,编译器会自动合成您的自定义类型的可哈希性和要求:
对于结构,其所有存储属性都必须符合哈希表。
对于枚举,其所有关联值都必须符合哈希表。(没有关联值的枚举即使没有声明也具有哈希一致性。)
要自定义类型的哈希一致性,要在不符合上面列出的条件的类型中采用哈希,或者要扩展现有类型以符合哈希,请在自定义类型中实现哈希(in to:)方法。
在散列(into:)实现中,在提供的散列器实例上使用类型的基本组件调用combine(u:)。为了确保您的类型满足哈希和等价协议的语义要求,最好还自定义类型的等价一致性以匹配。
例如,考虑描述按钮网格中位置的网格点类型。以下是网格点类型的初始声明:
1 //X-Y坐标系中的点。 2 struct GridPoint { 3 var x: Int 4 var y: Int 5 }
您想创建一组用户已经点击的网格点。由于GridPoint类型尚不可哈希,因此不能在集合中使用。要添加哈希一致性,请提供一个==运算符函数并实现哈希(into:)方法。
1 extension GridPoint: Hashable { 2 static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { 3 return lhs.x == rhs.x && lhs.y == rhs.y 4 } 5 6 func hash(into hasher: inout Hasher) { 7 hasher.combine(x) 8 hasher.combine(y) 9 } 10 }
本例中的hash(into:)方法将网格点的x和y属性馈送到提供的散列器中。这些属性与在==运算符函数中用于测试相等性的属性相同。
现在,网格点符合哈希协议,您可以创建一组以前点击过的网格点。
1 var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)] 2 let nextTap = GridPoint(x: 0, y: 1) 3 if tappedPoints.contains(nextTap) { 4 print("Already tapped at (\(nextTap.x), \(nextTap.y)).") 5 } else { 6 tappedPoints.insert(nextTap) 7 print("New tap detected at (\(nextTap.x), \(nextTap.y)).") 8 } 9 // Prints "New tap detected at (0, 1).")
示例:
1 class Point:Hashable 2 { 3 static func == (lhs: Point, rhs: Point) -> Bool { 4 return lhs.X == rhs.X && lhs.Y == rhs.Y 5 } 6 7 func hash(into hasher: inout Hasher) 8 { 9 hasher.combine(x) 10 hasher.combine(y) 11 } 12 13 init(_ x:Int,_ y:Int) 14 { 15 self.x = x 16 self.y = y 17 } 18 19 private var x:Int = 0 20 var X:Int 21 { 22 get{return x} 23 set{x = newValue} 24 } 25 26 private var y:Int = 0 27 var Y:Int 28 { 29 get{return y} 30 set{y = newValue} 31 } 32 }
点击:Playground测试
1 var set:Set<Point> = Set<Point>() 2 let point1:Point = Point(1,1) 3 let point2:Point = Point(2,2) 4 let point3:Point = Point(1,1) 5 print(point1 == point2) 6 //Print false 7 print(point1 == point3) 8 //Print true 9 set.insert(point1) 10 set.insert(point2) 11 set.insert(point3) 12 print(set) 13 //[__lldb_expr_66.Point, __lldb_expr_66.Point]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)