美文网首页
存储 dict 的元素前是计算 key 的 hash 值?

存储 dict 的元素前是计算 key 的 hash 值?

作者: display3d | 来源:发表于2018-09-13 18:53 被阅读0次

dict 的高性能与其存储方式是分不开的,我们知道 dict 的存储是基于哈希表(又称散列表),需要计算 hash 值,那么是计算谁的 hash 值呢?是像别人说的:存储 dict 元素前计算 key 的 hash 值?

验证

这里先创建个字典

>>> my_dict = {'a': 'apple', 'b': 'banana'}

由于哈希表是一块连续的内存空间(数组),在不考虑 hash 值冲突的情况下,如果计算的是 key 的 hash 值,那么:'a' 的 hash 值与 'b' 的 hash 值之间的差值'a' 的内存地址与 'b' 的内存地址之间的差值(可理解为内存地址里的距离) 相等才对,也就是说以下的等式成立才对

hash('a') - hash('b') == id('a') - id('b')

但事实上面等式返回的是 False

>>> hash('a') - hash('b') == id('a') - id('b')
False

先看看其中各项的具体值是多少

>>> hash('a')
-7336862871683211644
>>> hash('b')
3607308758832868774
>>> id('a')
1290454097736
>>> id('b')
1290454096056
>>> id('a') - id('b')
1680
>>> hash('a') - hash('b')
-10944171630516080418

可以很明显得看到差距还是挺大的
这说明计算的不是 key 的 hash 值(这种说法不够严谨),那计算的是什么呢?

计算的是 key 所在内存地址的 hash 值

在不考虑 hash 冲突的情况下, 'a' 所在内存地址的 hash 值与 'b' 所在内存地址的 hash 值之间的差值'a' 的内存地址与 'b' 的内存地址之间的差值 相等,也就是说以下的等式成立才对

hash(id('a')) - hash(id('b')) == hash(id('a')) - hash(id('b'))
>>> hash(id('a')) - hash(id('b')) == hash(id('a')) - hash(id('b'))
True
>>> id('a') - id('b')
1680
>>> hash(id('a')) - hash(id('b'))
1680

下面再多验证几个

>>> my_dict['c'] = 'cherry'
>>> hash(id('b')) - hash(id('c')) == hash(id('b')) - hash(id('c'))
True
>>> id('b') - id('c')
791760
>>> hash(id('b')) - hash(id('c'))
791760
>>> a['d'] = 'date'
>>> hash(id('d')) - hash(id('c')) == hash(id('d')) - hash(id('c'))
True
>>> id('d') - id('c')
1400
>>> hash(id('d')) - hash(id('c'))
1400

到这里就可以证明上面的结论

为何计算的是 key 所在的内存地址的 hash 值?

比如上面的'a'(1 个字符) 明显比其所在的内存地址 1290454097736(13 个字符)要短。短的计算不是更快吗?
记住一句话:Python 中一切皆对象,'a'是个 str 对象,1290454097736 是个 int 对象

>>> type('a')
<class 'str'>
>>> type(id('a'))
<class 'int'>

一个对象里不是仅仅存储对应值,它还有很多属性(含方法),来看看谁的属性多

>>> len(dir('a'))
77
>>> len(dir(id('a')))
70

str 对象比 int 对象多 7 个属性

它们都有个叫 __sizeof__() 的魔法方法,用于获取当前对象所占用的内存空间大小(字节)

>>> id('a').__sizeof__()
32
>>> 'a'.__sizeof__()
50

从上面可以发现:虽然 'a' 看起来只有 1 个字符,但其占用的内存空间要大于其内存地址 id('a') 所占用的空间

当然这不是主要原因,Python 解释器会将其转换为适当的数据类型再进行 hash 计算

不过,dict 的 key 不仅仅可以是 str 对象,也可以是 int、bytes、fromzenset 等这些可哈希(hashable)对象,可哈希对象都是不可变(immutable)对象(注意:反之不一定成立,如 tuple),不可变对象内存地址不变。大多数情况下,相比计算这些不同对象类型的 hash 值,直接计算对象所在内存地址(整数)的 hash 值性能更高,这也就是为什么不是计算 key 的 hash 值,而是计算 key 所在内存地址的 hash 值

阅读更多

微信公众号:display3d

相关文章

  • 存储 dict 的元素前是计算 key 的 hash 值?

    dict 的高性能与其存储方式是分不开的,我们知道 dict 的存储是基于哈希表(又称散列表),需要计算 hash...

  • (17)Redis的rehash与ConcurrentHashM

    dict是Redis的hash数据结构,用key值计算hashkey,元素插入到某个hash链上(拉链法解冲突)。...

  • Python中Dict与Set

    1.dict的赋值、访问、更新、删除 dict是key-value形式的存储结构,value值通过key来赋值、访...

  • 利用一致性hash把不同分类的数据存储到redis集群

    本文是把不同的分类作为一致性hash的key。example: redis 分布式集群存储是通过计算每个存储值的h...

  • MySQL Hash索引 vs B-Tree索引

    Hash 索引通过 hash 算法计算 hash 值,存储的索引应该也是 hash 值,查找时先计算查找字段的 h...

  • Dict和Set

    Dict Python内置了字典Dict(全称Dictionary),使用键-值(key-value)存储,具有极...

  • BM100 设计LRU缓存结构

    基本思想:hash结构m_db存储key-value。链表结构m_link存储最近set的key值,key值在链表...

  • 熟记1

    dict是用空间来换取时间的一种方法。 通过key计算位置的算法称为哈希算法(Hash)。 set和dict的唯一...

  • dictionary总结一下

    dict的支持,dict全称dictionary,使用键-值(key-value)存储,具有极快的查找速度。 1、...

  • shardjedis常用操作

    shardjedis采用一致hash算法实现key的分片,通过计算key的hash值将key分布到不同的redis...

网友评论

      本文标题:存储 dict 的元素前是计算 key 的 hash 值?

      本文链接:https://www.haomeiwen.com/subject/hgbwgftx.html