Swift 是一个强类型的语言,它通过协议(Protocol)为类型提供功能扩展的强大能力。本篇文章将深入解析四个在开发中非常常见也非常重要的协议:Equatable
、Hashable
、Identifiable
和 Comparable
。掌握它们不仅可以写出更简洁、更强大的代码,也能够更好地与 Swift 标准库(如数组、集合、字典等)进行交互。
Equatable 协议
什么是 Equatable
?
Equatable
是 Swift 中用于判断**两个值是否"相等"**的协议。只要一个类型遵循了 Equatable
,你就可以使用 ==
和 !=
运算符来比较两个该类型的实例是否相等。
Swift 中大多数内建类型(如 Int
、String
、Double
、Array
、Dictionary
等)都已经默认实现了 Equatable
。
语法定义
swift
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
这表示:你需要实现一个静态函数 ==
,接收两个同类型的参数,返回它们是否相等的布尔值。
使用场景
- 比较两个结构体或枚举是否"内容一致"
- 在集合操作中使用,如
contains()
、firstIndex(of:)
- 用于控制流中,如
if x == y { ... }
- 搭配
Hashable
自动派生哈希值逻辑
自动合成 ==
当你的结构体中所有属性都遵循 Equatable
时,Swift 会自动为你合成 ==
运算符的实现,不需要手动写。
swift
struct Person: Equatable {
let name: String
let age: Int
}
// 编译器合成逻辑相当于:
/*
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name && lhs.age == rhs.age
}
*/
let p1 = Person(name: "Alice", age: 25)
let p2 = Person(name: "Alice", age: 25)
let p3 = Person(name: "Bob", age: 30)
print(p1 == p2) // true
print(p1 == p3) // false
手动实现 ==
(部分属性比较)
有时候你不想比较所有属性,只关注关键字段,这种情况下可以自定义 ==
实现。
swift
struct Book: Equatable {
let title: String
let author: String
let isbn: String
// 只比较 ISBN,忽略作者和书名
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.isbn == rhs.isbn
}
}
这对某些实际场景非常有用,比如比较唯一标识符是否一致、或者忽略元数据差异。
注意事项
规则 | 是否自动合成 |
---|---|
所有属性都遵守 Equatable |
✅ 是 |
有任意一个属性不符合 Equatable |
❌ 否 |
已自定义 == 实现 |
❌ 编译器不会再自动合成 |
引用类型 class 默认不合成 == (需手动) |
❌ 否 |
与引用类型区别
对于 class
(引用类型):
==
不会自动合成,即使所有属性都符合Equatable
;- 默认比较的是内存地址是否相同 (使用
===
); - 如果你希望比较内容一致性,必须手动实现
Equatable
。
Hashable 协议
什么是 Hashable?
Hashable
协议允许你的类型用于集合类型如 Set
,或作为 Dictionary
的键(key)。它要求类型能够生成一个哈希值,供底层哈希表结构判断元素存储位置与是否相等。
当一个类型遵循 Hashable
,它也必须遵循 Equatable
,因为哈希值相同的两个对象,还需通过 == 判断是否真的"相等"。
语法定义
swift
protocol Hashable: Equatable {
func hash(into hasher: inout Hasher)
}
使用场景
• Set 中的元素类型 Element 必须是 Hashable
• Dictionary<Key, Value> 中的 Key 类型必须是 Hashable
• 快速查找/去重(基于哈希表)
自动合成 Hashable
当一个结构体中所有属性本身都是 Hashable 类型时,Swift 会自动合成 hash(into:) 和 ==。
swift
struct User: Hashable {
let id: Int
let username: String
}
var users: Set<User> = []
users.insert(User(id: 1, username: "Tom"))
users.insert(User(id: 2, username: "Jerry"))
// User(id: 1, username: "Tom") == User(id: 1, username: "Tom") → true
此时,Swift 自动合成的等价代码:
swift
extension User {
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(username)
}
static func == (lhs: User, rhs: User) -> Bool {
return lhs.id == rhs.id && lhs.username == rhs.username
}
}
自定义哈希逻辑
有时你只希望根据关键字段判断相等性,例如只按 id 判断唯一性:
swift
struct Product: Hashable {
let id: Int
let name: String
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Product, rhs: Product) -> Bool {
return lhs.id == rhs.id
}
}
此时 Set 中即使 name 不同,只要 id 相同就认为是重复元素。
注意事项
- Hashable 自动合成仅适用于 所有属性都是 Hashable 的结构体或枚举;
- 如果你手动实现了 hash(into:),就要确保 == 与哈希值的一致性(即"相等的对象必须有相同的哈希值");
- 引用类型 class 也可以遵循 Hashable,但不会自动合成,需要你手动实现;
- 如果类型不符合 Hashable,就不能作为 Set 元素或 Dictionary 的键,编译报错。
容器类型 | 元素/键的类型约束 |
---|---|
Set | Element: Hashable |
Dictionary<Key, Value> | Key: Hashable |
Identifiable 协议
什么是 Identifiable?
Identifiable
协议定义了一个稳定的唯一标识符。这在 SwiftUI 中尤其有用,用于标识列表(List
、ForEach
)中的元素。
语法定义
swift
protocol Identifiable {
associatedtype ID: Hashable
var id: ID { get }
}
使用场景
- SwiftUI 的
List
和ForEach
自动识别元素 - 标识数据库或网络对象
- 建模唯一对象实体
示例
swift
struct Task: Identifiable {
let id: UUID = UUID()
let title: String
}
在 SwiftUI 中的应用:
swift
struct TaskListView: View {
let tasks = [
Task(title: "Write Blog"),
Task(title: "Read Book")
]
var body: some View {
List(tasks) { task in
Text(task.title)
}
}
}
Comparable 协议
什么是 Comparable?
遵循 Comparable
协议的类型可以进行大小比较,比如 <
、<=
、>
、>=
。它默认要求你实现 <
运算符。
语法定义
swift
protocol Comparable: Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
}
使用场景
- 数组排序
sorted()
、sort()
- 最值计算:
min
、max
- 排序操作中用作键值
示例
swift
struct Score: Comparable {
let value: Int
static func < (lhs: Score, rhs: Score) -> Bool {
return lhs.value < rhs.value
}
}
let scores = [Score(value: 10), Score(value: 30), Score(value: 20)]
let sorted = scores.sorted()
你也可以让多个属性参与比较:
swift
struct Player: Comparable {
let name: String
let score: Int
static func < (lhs: Player, rhs: Player) -> Bool {
if lhs.score == rhs.score {
return lhs.name < rhs.name
}
return lhs.score < rhs.score
}
}
总结
协议 | 作用 | 常见应用 |
---|---|---|
Equatable | 判断两个值是否相等 | if 判断、查找、比较等 |
Hashable | 提供哈希值用于集合、字典等 | Set、Dictionary 的键、去重 |
Identifiable | 提供唯一标识符 | SwiftUI List、模型标识 |
Comparable | 值之间的大小关系比较 | 排序、筛选、最值计算等 |
通过为你的自定义类型实现这些协议,不仅可以获得 Swift 提供的强大集合操作能力,还能让代码更具表达力与扩展性。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。