Lua中的数据结构—table
lua定义了以下几种类型:
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
tables的实现被分成了两个部分: 核心由ltable.c完成,提供了table的基本存取方法, 外部table库(ltablib.c)提供了辅助操作接口(concat, foreach, foreachi, getn, maxn, insert, remove, setn, sort).
ltable.c table的主要代码在这里
lobject.h lua的基础对象的定义,包括table
直接上
一.table的结构定义:
typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
lu_byte lsizenode; /* log2 of size of `node' array
*/
struct Table *metatable;
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
GCObject *gclist;
int sizearray; /* size of `array' array */
} Table;
这里做几个说明:
1.其中lu_byte定义:
luatypedef unsigned char lu_byte;
2.CommonHeader:
为所有可回收资源提供标记头
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
3.metatable是元表,作用请自行谷歌:
struct Table *metatable;
4.TValue
typedef struct lua_TValue {
TValuefields;
} TValue;
TValuefields:
#define TValuefields Value value; int tt
Value:
typedef union {
GCObject *gc;
void *p;
lua_Number n;
int b;
} Value;
其实TValue是一个结构体,里面是TValuefields,它由一个宏定义完成,包括Value和一个int值。
Value是一个联合,它是里面数据类型的一种,可以是指针、数字等,这里不介绍具体用法。
5.Node
typedef union TKey {
struct {
TValuefields;
struct Node *next; /* for chaining */
} nk;
TValue tvk;
} TKey;
typedef struct Node {
TValue i_val;
TKey i_key;
} Node;
每个Node都是一个键值对。
lua之所以用这种方式来表示Node,感觉一个很重要的原因是为了适应不同参数类型的接口。
TKey中tvk是这个key的值,nk中的next则指向key冲突的下一个节点。lua的hash表的hash算法比较特别,一般的hash表都是根据key算出hash(key),然后把这个key放在hash表的hash(key)位置上,如果有冲突的话,就放在hash(key)位置的链表上。
但是lua的hash表中,如果有冲突的话,lua会找hash表中一个空的位置(从后往前找,假设为x),然后把新的key放在这个空的位置x上,并且让hash表中hash(key)处的节点的nk.next指向x。这个意思就是,假如有冲突的话,不用重新分配空间来存储冲突的key,而是利用hash表上未用过的空格来存储。但是这样会引入另外一个问题,本来key是不应该放在x的,假设有另外一个key2,hash(key2)算出来的位置也在x的话,那就表示本来x这个位置应该是给key2的,但是由于x被key占用了,导致key2没地方放了。这时候lua的处理方式是把key放到另外一个空格,然后让key2占回x。当hash表已经没有空格的时候,lua就会resize这个hash表。这样做的好处主要是不用动态申请内存空间,hash表初始化的时候有多少内存空间就用多少,不够就resize这个hash表。
小结