当前位置: 首页 > 面试题库 >

如何在Swift中处理字典的哈希冲突

上官思博
2023-03-14
问题内容

我的自定义结构实现了Hashable
Protocol
。但是,当在键中插入键时发生哈希冲突时Dictionary,不会自动处理它们。我该如何克服这个问题?

背景

我之前曾问过这个问题,
如何在Swift中为Int数组(自定义字符串struct)实现哈希协议。后来我添加了自己的答案,似乎很有效。

但是,最近我hashValue在使用时发现了一个细微的碰撞问题Dictionary

最基本的例子

我已将代码简化为以下示例。

定制结构

struct MyStructure: Hashable {

    var id: Int

    init(id: Int) {
        self.id = id
    }

    var hashValue: Int {
        get {
            // contrived to produce a hashValue collision for id=1 and id=2
            if id == 1 {
                return 2 
            }
            return id
        }
    }
}

func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

请注意,为了使等式运算符(==)重载,以使其符合Hashable协议所要求的Equatable协议,它具有全局功能。

微妙的字典关键问题

如果我创建一个Dictionarywith MyStructure作为键

var dictionary = [MyStructure : String]()

let ok = MyStructure(id: 0)            // hashValue = 0
let collision1 = MyStructure(id: 1)    // hashValue = 2
let collision2 = MyStructure(id: 2)    // hashValue = 2

dictionary[ok] = "some text"
dictionary[collision1] = "other text"
dictionary[collision2] = "more text"

print(dictionary) // [MyStructure(id: 2): more text, MyStructure(id: 0): some text]
print(dictionary.count) // 2

相等的哈希值会导致collision1密钥被密钥覆盖collision2。没有警告。如果这样的冲突在带有100个键的字典中仅发生一次,则很容易错过。(花了我一段时间才注意到这个问题。)

字典文字的明显问题

但是,如果我用字典文字重复此操作,则问题将变得更加明显,因为会引发致命错误。

let ok = MyStructure(id: 0)            // hashValue = 0
let collision1 = MyStructure(id: 1)    // hashValue = 2
let collision2 = MyStructure(id: 2)    // hashValue = 2

let dictionaryLiteral = [
    ok : "some text",
    collision1 : "other text",
    collision2 : "more text"
]
// fatal error: Dictionary literal contains duplicate keys

我的印象是,不必hashValue总是返回唯一值。例如,马特·汤普森(Matt
Thompson)说,

关于实现自定义哈希函数的最常见误解之一来自……认为哈希值必须是不同的。

尊敬的SO用户@Gaffa说,处理哈希冲突的一种html" target="_blank">方法是

认为哈希码是非唯一的,并对实际数据使用相等比较器来确定唯一性。

我认为问题是快速可哈希协议哈希函数是否需要返回唯一值?在撰写本文时尚未得到足够的答复。

阅读完SwiftDictionary问题后,如何处理哈希冲突?),我认为Swift会自动处理与的哈希冲突Dictionary。但是显然,如果我使用的是自定义类或结构,那是不对的。

func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

是否为每个字典键查找或仅在发生哈希冲突时才调用此函数?(更新:请参阅此问题)

当(且仅当)哈希冲突发生时,我该怎么做才能确定唯一性?


问题答案:

func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
}

请注意,为了使等式运算符(==)重载,以使其符合Hashable协议所要求的Equatable协议,它的全局功能。

您的问题是不正确的 相等 实现。

哈希表(例如Swift字典或Set)需要单独的 相等哈希 实现。

哈希 使您靠近要查找的对象; 平等为 您提供了您正在寻找的确切对象。

您的代码对 哈希相等 使用相同的实现,这将确保发生冲突。

要解决此问题,请实现 相等性 以匹配确切的对象值(但是您的模型定义了相等性)。例如:

func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
    return lhs.id == rhs.id
}


 类似资料:
  • 问题内容: 当大小超过maxthreshold值时,如何在哈希表或哈希表中进行重新哈希处理? 是否所有对都已复制到新的存储桶阵列中? 编辑: 重新哈希后,同一存储桶(位于链接列表中)中的元素会发生什么情况?我的意思是说,他们在重新哈希处理后会留在同一个桶中吗? 问题答案: 问题中的最大阈值称为负载系数。 建议负载系数约为0.75。负载因子定义为(m / n),其中n是哈希表的总大小,m是在需要增加

  • 问题内容: 我正在开发一个Android应用程序,并且在发送到数据库之前有一些我想加密的字符串。我想要一个安全,易于实现的东西,每次传递相同的数据时都会生成相同的东西,并且无论传递给它的字符串有多大,最好都会使字符串保持恒定的长度。也许我正在寻找一个哈希。 问题答案: 此代码段为任何给定的字符串计算md5 资料来源:http : //www.androidsnippets.com/snippets

  • 问题内容: 我想用JavaScript创建地图对象。我想到了以下想法: 但是我怎么才能找到一个特定的密钥是否存在? 问题答案: 如果要命名键,请不要使用数组,而应使用普通对象。 然后:

  • 问题内容: 你们中 有谁 知道如何在 AngularJS中 很好地处理锚点哈希链接吗? 我为简单的常见问题解答页面添加了以下标记 单击上面的任何链接时,AngularJS会拦截并将我路由到一个完全不同的页面(在我的情况下是404页,因为没有路由与这些链接匹配。) 我的第一个想法是创建一个匹配“ / faq /:chapter ” 的路由,并在相应的控制器中检查匹配的元素之后,然后使用jQuery向

  • 问题内容: 在Objective-C中,它看起来像这样: 我需要Swift这样的东西,可以吗? 请显示工作示例。 问题答案: 您的Objective-C代码(使用类别)可以直接转换为Swift(使用扩展名)。 首先,您必须创建一个“桥接头”并添加 然后: 这可以写得更短和更快速 Swift 2更新: 要返回以Base-64编码的字符串而不是十六进制编码的字符串,只需替换 与 Swift 3更新:

  • 问题内容: 如何转义URL查询字符串中发送的哈希符号(有时称为数字符号或井号)? 问题答案: 百分比编码。将哈希替换为。