哈希表中的存储桶是否包含指针或值?

时间:2018-11-03 18:43:22

标签: python hashtable

我正在学习python,提出的要点之一是变量名分别存储在内存中,即它们所引用的对象。也就是说,它们有一些指向内存中不同区域的指针,这些区域实际上是所存储的对象。

我现在正在阅读什么是哈希表以及如何实现它们的(基本)知识。这个问题的答案确实很有帮助:How does a hash table work?

我从中得出的结论是,如果我有一个键值对,那么哈希本质上会将索引转换为键,然后将该键存储在数组中。因此,如果“ key1”的索引为0,则a [0]包含实际值“ value1”。

但是,我不确定是否确实如此,还是不​​确定像python中的变量那样,数组中的值实际上不表示'value1',而是一些指向内存中'value1'位置的指针' 被储存了。还是去'key1'->数组索引-> a [array index]->值,还是去'key1'->数组索引-> a [array index]->指针地址- ->'value1'存储在由指针地址确定的存储位置中吗?

作为后续问题,如果是后者,那是否意味着存储在哈希表中的值实际上分散在整个内存中,而不是顺序存储?非常感谢,如果问题很明显,我们深表歉意。

2 个答案:

答案 0 :(得分:1)

警告:这个答案可能有点令人困惑,因为要考虑两个单独的问题:

  • Python具有内置的哈希表类型dict。它的内部实现是用C编写的(至少对于CPython而言),并使用了一些无法直接用Python编写的技巧。有关多年来使用的(几种)实现的详细信息,请参见How are Python's Built In Dictionaries Implemented

  • Python作为一种语言,大多数没有数组。 1 链接问题的某些答案是用数组表示的,而Python的内置是 list 类型可以像数组一样使用,因此可以用作模型。那就是我在这里要做的。

因此,让我们开始制作一个空的伪数组:[]。我们将其绑定到一些“类似数组”的名称:

a = []

然后回答您的问题。


1 array模块提供C样式的数组。有关详细信息,请参见the array documentation


  

我从中得出的结论是,如果我有一个键值对,那么哈希本质上会将索引转换为键,然后将该键存储在数组中。

反之亦然:哈希将密钥(“太大”)转换为计算机可以更轻松,直接地处理的更小(即哈希)值。它将密钥转换为索引。不过,您在问题的下一部分中对此是正确的:

  

因此,如果'key1'的索引为0,则a[0]包含实际值'value1'

通常,是的。但是,如果哈希表既可以用于命中,也可以用于未命中,并且某些其他键(例如'1frotz')也可以 转换为索引0,则必须在{{ 1}},或保留实际键的并行数组或沿这些方向排列的东西,以确保a[0]持有a[0]而不持有其他键/值对。也就是说,在Python中,我们可以这样做:

'key1'

当然,我们还需要能够将项目放入哈希表。通常,当我们这样做时,如果键值对不适合,我们将扩展表格,因此,我们代替了上面的i = hash(key) % tablesize # assuming a fixed table size assert i < len(a) # this is going to fail since len(a) == 0! kv_pair = a[i] assert kv_pair[0] == key ... use kv_pair[1], which holds the value ... ,我们这样做:

assert

当我们点击“复杂部分”时,或者数组本身太小,或者我们要使用的插槽已经用于某些 other 键。

在这里哈希表变得花哨。有些使用辅助哈希函数,执行称为 re-hashing 的操作,并探测其他表槽(在这种情况下,我们希望以非零的表大小开始)。有些将表扩展到位。同样,要了解Python的实际功能,请查看其他问题及其答案。

  

但是,我不确定是否确实如此,还是不​​确定像python中的变量那样,数组中的值实际上不表示'value1',而是一些指向内存中'value1'位置的指针' 被储存了。 ...

由于Python允许字典中使用动态类型,因此任何散列键的值槽中肯定会存储指向实际值的指针,而不是该值的副本。不同类型的值具有不同的基础大小。您可以使用def find_value(key): if len(a) > 0: i = hash(key) % len(a) kv_pair = a[i] if kv_pair is not None and kv_pair[0] == key: return kv_pair[1] return None def insert_kv_pair(key, value): if len(a) > 0: i = hash(key) % len(a) kv_pair = a[i] if kv_pair is None: # not in the table yet a[i] = [key, value] # so put it in return if kv_pair[0] == key: # already in the table kv_pair[1] = value # so replace the value return ... the complicated parts go here ... 查看类型的大小(但也请参见How do I determine the size of an object in Python?):

sys.getsizeof

由于大小在整个地图上都不同,因此Python只会将指针存储在给定键的字典的“值”插槽中。

  

...这是否意味着散列表中存储的值实际上分散在整个内存中,而不是顺序存储?

是的。在内部Python实现中,仅哈希值和一对键/值指针按顺序存储。

答案 1 :(得分:1)

如果您查看所看到的Python字典的基本查找功能的source code,它们将为实际值分配一个指针。

在该方法中,您还可以使用给定的键查看查找的所有步骤。所以我认为你的假设

'key1' --> array index --> a[array index] --> pointer address --> 'value1'

是正确的。