如何在Swift中按属性对对象数组排序:深入底层原理
你好,同行!如果你曾经用Swift编写过显示用户列表、商品列表或任务列表的应用程序,那么你肯定遇到过需要对这些数据进行排序的情况。“How to sort an array of custom objects by property value?”这个问题是Stack Overflow上最热门的问题之一。这并非偶然:乍一看似乎很简单,但深入挖掘后,你会发现一系列丰富的可能性。
今天,我们不仅仅给出一个现成的答案。我们将深入探讨Swift底层的排序机制,了解涉及哪些原理,以及如何编写不仅可用,而且高效优雅的代码。开始吧!
什么是Swift中的对象数组排序?
想象一下:你有一个User对象数组。每个用户都有姓名、年龄和评分。你需要将它们按年龄(从小到大)排序后显示在屏幕上。
在Swift中,这可以通过内置方法sort()(原地排序数组)和sorted()(返回新的排序后数组)来实现。两者都接受一个用于确定顺序的闭包。
struct User { let name: String let age: Int let rating: Double}
let users = [ User(name: "安娜", age: 25, rating: 4.5), User(name: "鲍里斯", age: 30, rating: 3.8), User(name: "维卡", age: 22, rating: 4.9)]
// 按年龄排序(升序)let sortedByAge = users.sorted { $0.age < $1.age }print(sortedByAge.map { $0.name }) // ["维卡", "安娜", "鲍里斯"]就是这样。但让我们深入了解一下这里实际发生了什么。
sort()底层工作原理:算法与闭包
Swift中的sort()方法使用混合算法TimSort(以Tim Peters命名)。这是一种自适应、稳定的排序算法,结合了归并排序和插入排序。它在部分有序的数据上尤其高效——例如新闻推送和任务列表!
你传递的闭包是一个排序谓词:一个接受两个元素并返回true(如果第一个应排在第二个之前)的函数。Swift会多次调用此闭包来比较元素对。
重要提示:如果你的对象符合Comparable协议,甚至可以完全省略闭包:
struct User: Comparable { let name: String let age: Int static func < (lhs: User, rhs: User) -> Bool { return lhs.age < rhs.age }}
let sortedUsers = users.sorted() // 现在更简单了但如果需要在运行时按不同属性排序呢?这时键路径(KeyPath)就派上用场了。
使用KeyPath排序:优雅与灵活
从Swift 4开始,我们有了KeyPath。这是一种安全引用对象属性的方式。将KeyPath与闭包结合,可以编写通用的排序函数:
extension Array { mutating func sort<T: Comparable>(by keyPath: KeyPath<Element, T>, ascending: Bool = true) { sort { ascending ? $0[keyPath: keyPath] < $1[keyPath: keyPath] : $0[keyPath: keyPath] > $1[keyPath: keyPath] } } func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>, ascending: Bool = true) -> [Element] { return sorted { ascending ? $0[keyPath: keyPath] < $1[keyPath: keyPath] : $0[keyPath: keyPath] > $1[keyPath: keyPath] } }}
// 使用方式:users.sort(by: \\.age) // 按年龄(升序)let sortedByRating = users.sorted(by: \\.rating, ascending: false) // 按评分(降序)很优雅吧?类型安全