您的位置:首页 > 编程语言 > Lua

lua源码阅读(4)---表

2017-09-18 16:21 218 查看
lua中只有一种数据结构,表,可以可以存储任意类型,只要键不是nil。在Lobject.h中,有表的定义:

typedef struct Table {
CommonHeader;

//表示存在哪些元方法
lu_byte flags;  /* 1<<p means tagmethod(p) is not present */

//  log2( node数组大小)
lu_byte lsizenode;  /* log2 of size of `node' array */

//元表
struct Table *metatable;

//数组部分
TValue *array;  /* array part */

// Node数组
Node *node;

Node *lastfree;  /* any free position is before this position */

GCObject *gclist;

//array大小
int sizearray;  /* size of `array' array */

} Table;
lua中的表包含了两部分,一个是数组部分,存储在array指向的数组中,另一个是hash表部分,存储在node指向的数组中。其中Node类型如下:

typedef union TKey {

struct {
TValuefields;   //Value value; int tt
struct Node *next;  /* for chaining */
} nk;

TValue tvk;

} TKey;

typedef struct Node {
TValue i_val;
TKey i_key;
} Node;
Node包含了key和value。

如在lua中 a={3,2,4,x=10},则3,2,4存储在数组部分,x=10存储在hash表部分

在ltable.c中,定义了一些对表的操作的函数。

luaH_set,;uaH_setnum.luaH_strstr是进行新增元素的操作。首先进行查找,如果表中没有改元素,则进行添加的操作,最终调用了newkey函数

static TValue *newkey (lua_State *L, Table *t, const TValue *key) {

//计算hash值,得到key所在的hash表中的位置
Node *mp = mainposition(t, key);

//如果该位置已经存储了元素
if (!ttisnil(gval(mp)) || mp == dummynode) {

Node *othern;

//获取一个空闲的位置
Node *n = getfreepos(t);  /* get a free place */

if (n == NULL) {  /* cannot find a free place? */
//扩展
rehash(L, t, key);  /* grow table */
return luaH_set(L, t, key);  /* re-insert key into grown table */
}

lua_assert(n != dummynode);
//#define key2tval(n)	(&(n)->i_key.tvk)
othern = mainposition(t, key2tval(mp));

if (othern != mp) {  /* is colliding node out of its main position? */
/* yes; move colliding node into free position */

while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */

gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */
*n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */
gnext(mp) = NULL;  /* now `mp' is free */
setnilvalue(gval(mp));
}
else {  /* colliding node is in its own main position */
/* new node will go into free position */
gnext(n) = gnext(mp);  /* chain new position */
gnext(mp) = n;
mp = n;
}
}

gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;

luaC_barriert(L, t, key);
lua_assert(ttisnil(gval(mp)));
return gval(mp);
}


首先根据key 计算位置,如果该位置没有存储,则直接存储,否则,发生了hash冲突,需要找到新的空闲位置,存储,加入到链表中。该链表比较特殊,是通过数组实现的。

结构如图



在hash部分的数组中,每一个位置都有一个指针,用于形成链表。这样,hash值相同的元素存储在数组中后,又形成了一条链表。整个hash部分会形成多条链表。这种用数组实现链表的方式与普通的链地址法有些差异。

理解了table的存储结构,其余的操作也就比较好理解了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: