【Lua 5.3源码】table实现分析

1.table的特性

  1. 在Lua中table是个非常重要的类型,通过使用table的一些特性可以实现许多数据结构,例如map,array queue,stack等。
  2. 通过使用者角度来讲,table既可以当作array使用也可以当作map使用,那么对于设计者来讲,那么需要保证table的高效率的查找、插入、遍历。
  3. 当然,table的设计者还提出了metatable(元表)的概念,以供使用者可以用来实现继承、操作符重载等设计,不过metatable暂时不在这边文章进行讨论。



2.table的定义

typedef union TKey {
   
   
  struct {
   
   
    TValuefields;
    int next;  /* 用于标记链表下一个节点 */
  } nk;
  TValue tvk;
} TKey;

typedef struct Node {
   
   
  TValue i_val;
  TKey i_key;
} Node;


typedef struct Table {
   
   
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */
  lu_byte lsizenode;  /* log2 of size of 'node' array */
  unsigned int sizearray;  /* size of 'array' array */
  TValue *array;  /* array part */
  Node *node;
  Node *lastfree;  /* any free position is before this position */
  struct Table *metatable;
  GCObject *gclist;
} Table;
  • flags : 元方法的标记,用于查询table是否包含某个类别的元方法
  • lsizenode : (1<<lsizenode)表示table的hash部分大小
  • sizearray : table的数组部分大小
  • array : table的array数组首节点
  • node :table的hash表首节点
  • lastfree : 表示table的hash表空闲节点的游标
  • metatable : 元表
  • gclist : table gc相关的参数

为了提高table的插入查找效率,在table的设计上,采用了array数组和hashtable(哈希表)两种数据的结合。

所以table会将部分整形key作为下标放在数组中, 其余的整形key和其他类型的key都放在hash表中。



3.hash表结构

在table中的实现中,hash表占绝大部分比重,下面是table中hash表的结构示意简图:
在这里插入图片描述
hash表在解决冲突有两个常用的方法:

  • 开放定址法:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探查到开放的 地址则表明表中无待查的关键字,即查找失败。
  • 链地址法:又叫拉链法,所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0…m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于 1,但一般均取α≤1。

简单对比可以发现以上两种方法的优缺点:

开放定址法相比链地址法节省更多的内存,但在插入和查找的时候拥有更高的复杂度。

但是table中的hash表的实现结合了以上两种方法的一些特性:

  • 查找和插入等同链地址法复杂度。
  • 内存开销近似等同于开放定址法

原因是table中的hash表虽然采用链地址法的形式处理冲突,但是链表中的额外的节点是hash表中的节点,并不需要额外开辟链表节点;下面是TKey结构的介绍:
在这里插入图片描述
那么如何将hash表的空余节点利用起来作为链表的节点呢?这个算法的实现得益于lastfree这个指针的作用,后面会详细介绍。



4.table的创建

lua中通过luaH_new来创建一个新table:

Table *luaH_new (lua_State *L) {
   
   
  GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table));
  Table *t = gco2t(o);
  t->metatable = NULL;
  t->flags = cast_byte(~0);
  t->array = NULL;
  t->sizearray = 0;
  setnodevector(L, t, 0);
  return t;
}

此时,table中的array部分和hash部分都为空。



5.luaH_get分析

table中通过这个函数来从表中查找key对应的值;可以看到它会通过key的类型不同,从而进行不同的处理。

const TValue *luaH_get (Table *t, const TValue *key) {
   
   
  switch (ttype(key)) {
   
   
    case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key));
    case LUA_TNUMINT: return luaH_getint
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值