Lua表的底层实现

本文详细介绍了Lua中的核心数据结构——Table,包括其特点、数据结构和重要操作。Table既是容器,也可实现面向对象功能,其内部由数组和哈希表两部分组成。在插入键值时,Lua根据整数键和非整数键的不同规则决定存储位置。此外,文章还探讨了Table的动态扩展、for循环的工作原理等。
摘要由CSDN通过智能技术生成

Table结构

Table特点

容器功能:与其他语言相似,lua也内置了容器功能,也就是table。而与其他语言不同的是,lua内置容器只有table。正因为如此,为了适配不同的应用需求,table的内部结构也比较考究,分为了数组和哈希表两个部分,根据不同需求来决定使用哪个部分。
面向对象功能:与其他语言不同的时,lua并没有把面向对象的功能以语法的形式包装给开发者。而是保留了这样一种能力,待开发者去实现自己的面向对象。而这一保留的能力,也是封装在table里的:table里可以组合一个metatable,这个metatable本身也是一个table,它的字段用来描述原table的行为。
lua是一个短小精悍的语言,之所以把这么多功能全都聚合到table结构中,我的理解是,它不想让开发者去关心过多的类型,能够真正做到table在手,天下我有。同时,虽然table要兼具这么多的功能,但是lua在实现它的时候,其实是非常克制的,做到了真正的灵活可扩展,开发者可以在table提供的基础功能下扩展出非常多样性的结构和类型。

Table的数据结构

1.Table结构体
table是存放在GCObject里的。结构如下:

typedef struct Table {
   
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ 
  lu_byte lsizenode;  /* 以2的lsizenode次方作为哈希表长度 */
  struct Table *metatable /* 元表 */;
  TValue *array;  /* 数组 */
  Node *node; /* 哈希表 */
  Node *lastfree;  /* 指向最后一个为闲置的链表空间 */
  GCObject *gclist;
  int sizearray;  /* 数组的大小 */
} Table;

从table的结构可以看出,table在设计的时候以两种结构来存放数据。一般情况对于整数key,会用array来存放,而其它数据类型key会存放在哈希表上。并且用lsizenode作为链表的长度,sizearray作为数组长度。
2、Node结构体

typedef union TKey {
   
  struct {
   
    TValuefields;
    struct Node *next;  /* 指向下一个冲突node */
  } nk;
  TValue tvk;
} TKey;

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

Node结构很好理解,就是一个键值对的结构。主要是TKey结构,这里用了union,所以TKey的大小是nk的大小。并且实际上TValue与TValuefields是同一个结构,因此tvk与nk的TValuefields都是代表键值。而且这里有一个链表结构struct Node *next,用于指向下一个有冲突的node。

3.各成员的定义

CommonHeader:垃圾回收通用结构,详情参考本专栏数据结构篇。
flags:用于cache该表中实现了哪些元方法。
lsizenode:哈希表大小取log2(哈希表大小只会为2的次幂)
sizearray:数组大小(数组大小只会为2的次幂)
array:数组头指针
node:哈希表头指针
lastfree:哈希表可用尾指针,可用的节点只会小于该lastfree节点。
metatable:元表
gclist:GC的链表,用于垃圾回收。

大致的结构图如下:
在这里插入图片描述

Table的重要操作

1.创建table

table的创建通过lua_newtable函数实现。通过定位具体实现是在luaH_new这个函数进行table的创建。代码如下:

Table *luaH_new (lua_State *L, int narray, int nhash) {
   
  Table *t = luaM_new(L, Table);/* new一个table对象 */
  luaC_link(L, obj2gco(t), LUA_TTABLE);
  t->metatable = NULL;
  t->flags = cast_byte(~0);
  /* temporary values (kept only if some malloc fails) */
  t->array = NULL;
  t->sizearray = 0;
  t->lsizenode = 0;
  t->node = cast(Node *, dummynode);
  setarrayvector(L, t, narray);
  setnodevector(L, t, nhash);
  return t;
}

主要是对table进行初始化,其中setarrayvector是对数组大小进行设置,setnodevector是对hash表大小进行设置,具体代码如下:

/*
  设置数组的容量
*/
static void setarrayvector (lua_State *L, Table *t, int size) {
   
  int i;
  //重新设置数组的大小
  luaM_reallocvector(L, t->array, t->sizearray, size, TValue);
  //循环把数组元素初始化为nil类型
  for (i=t->sizearray; i<size; i++)
     setnilvalue(&t->array[i]);
  t->sizearray = size;
}

/*
 设置哈希表的容量
*/
static void setnodevector (lua_State *L, Table *t, int size) {
   
  int lsize;
  if (size == 0) {
     /* no elements to hash part? */
    t->node = cast(Node *, dummynode);  /* use common `dummynode' */
    lsize = 0;
  }
  else {
   
    int i;
    //实际大小转化为指数形式
    lsize = ceillog2(size);
    if (lsize > MAXBITS)
      luaG_runerror(L, "table overflow");
    //这里实际大小以2的lsize次方来算的
    
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值