我目前正在开发一个 Kotlin 项目,用于自动维护紧急位置数据。作为该项目的一部分,我需要比较两个 Json 对象集合,以确定需要添加/更新/删除哪些数据。集合A是现有数据的集合。集合 B 是来自作业的传入数据。虽然集合中的对象不相同,但它们具有可用于链接它们的唯一 ID。
对于 A 中存在但 B 中不存在的每个项目,我需要进行删除调用。对于 B 中但不在 A 中的每个项目,我需要进行创建。对于 A 和 B 中存在的每个项目,我需要进行更新。
我需要找到一种方法来确定所需的操作,每个操作都将涉及对第三方 API 的 HTTP 请求,并尽可能高效地执行所需的操作。我知道我可以通过迭代每个集合中的所有项目来简单地解决这个问题。然而,由于这将成为 AWS Lambda 的一部分,我认为这不会削减它。
使用 Kotlin 解决此类问题最有效的方法是什么?
如果我正确理解这个问题,它只是通过某种键来比较已经存储在内存中的两组对象。我们实际上并没有太多的火箭科学或性能优化可以在这里涉及。唯一的优化是不要将每个项目与每个项目进行比较(这将是
O(N*M)
),而是创建一个哈希集/映射并在其中进行搜索(O(N+M)
):
fun <T : Any> diffSetsBy(setA: Iterable<T>, setB: Iterable<T>, keySelector: (T) -> Any?): SetsDiff<T> {
val onlyA = setA.associateByTo(mutableMapOf(), keySelector)
val onlyB = mutableListOf<T>()
val both = mutableListOf<T>()
for (b in setB) {
if (onlyA.remove(keySelector(b)) != null) {
both += b
} else {
onlyB += b
}
}
return SetsDiff(onlyA.values, onlyB, both)
}
data class SetsDiff<T>(val onlyA: Iterable<T>, val onlyB: Iterable<T>, val both: Iterable<T>)
算法要求密钥在单个集合中不重复。我使用了传统的命令式代码,因为我没有找到一种方法可以使代码更实用或更惯用并保持可读性。另外,当我创建了一个通用的
diffSetsBy
函数时,返回 both
的对象对可能是有意义的,但我们的案例不需要它,而且我太懒了。
我们可以这样使用它:
fun main() {
val users = listOf(User("1", "James"), User("2", "Jonh"))
val newUsers = listOf(User("2", "John"), User("3", "Dave"))
val (toDelete, toAdd, toUpdate) = diffSetsBy(users, newUsers, User::login)
println(toDelete) // [User(login=1, name=James)]
println(toAdd) // [User(login=3, name=Dave)]
println(toUpdate) // [User(login=2, name=John)]
}
data class User(val login: String, val name: String)