表的构成
typedef union TKey {
struct {
TValuefields;
struct Node *next; /* for chaining */
} nk;
TValue tvk;
} TKey;
typedef struct Node {
TValue i_val;
TKey i_key;
} Node;
// lua table的基本数据结构
typedef struct Table {
//GC有关
CommonHeader;
GCObject *gclist;
//哈希部分
lu_byte lsizenode; /* log2 of size of `node' array */
Node *node; /* hash part */
Node *lastfree; /* any free position is before this position */
//数组部分
TValue *array; /* array part */
int sizearray; /* size of `array' array */
//元表和其他
struct Table *metatable;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
} Table;
数组部分
用于存放连续整数键(从1开始的)的值,这样可以提高访问整数键值的性能
数组内容是 TValue *array; 长度是int sizearray;
哈希部分
用于存放非整数键值和非连续整数见的值,nil不能作为键
哈希表存放在Node* node 数组,数组的大小是2^ lsizenode
lastfree的含义
空闲的node在lastfree的左边,lastfree指向的内存,以及右边的内存均已被使用
发生冲突,lastfree向左找,找到了空闲就停止,然后把元素塞进去
元表和其他部分
表的基本操作
表的创建和初始化
初始化的时候,哈希表lsizenode=0, 按照公式应该其大小为数组为1 。为了维护哈希表数组长度为2^lsizenode这个规则,哈希表Node指向一个共用的dummyNode结点
查询
下标
怎么知道我这个元素在哈希表的哪个下标?
index = hash_value & ((2 ^ lsizenode) - 1)
(2 ^ lsizenode)是哈希表数组大小
& ((2 ^ lsizenode) - 1) 相当于取mod,但是比取mod好
为啥要取mod,就是让哈希值能映射到数组内,不会飞出去
这里的 维护(2 ^ lsizenode) 这个大小的用处在这里体现了
如果 lsizenode是4(即哈希表大小为16),那么(2^
lsizenode) - 1
是15,二进制表示为 (1111)。任何数与 (1111) 进行按位与操作,都会得到一个在0到15之间的结果,这正好适合索引一个大小为16的数组。
例如key 为19
其实就是把超出数组长度的二进制部分数据直接给砍了,有点像计网掩码的意思
查找
是int类型
key为k,k如果小于arraySize 就直接返回array[k-1]
否则查找哈希表
首先index = k & (2^lsizenode-1) ,以hash[index]为起点查找
比较,如果不是就找next,next==0就认为找不到了
不是int 类型就直接找哈希表
在前面基础上 k 要自己调用哈希函数算一个哈希值出来key_hash
然后就是上面的步骤,index = key_hash & (2^lsizenode-1),
以hash[index]节点为起点......
插入
整数而且是在数组范围内
key为3 找到,刚好为空插入
超出找哈希表插入
发生哈希冲突,lastfree向左找然后插入,然后设置一下next
再次冲突,但是13 拿 7原本的座位,lastfree向左找新的空闲位置,让13去新的座位坐
7要坐自己的位置。这个过程中,5的next 重新设置为指向13 的2
参考和引用
图片和主要内容来自
构建Lua解释器Part4:Table设计与实现 (manistein.github.io)
如果侵权,请联系我删除