LUA的table实现
数据结构
下面的结构体是lua中用于表示一个table,主要关注里面的array和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 alimit; /* "limit" of 'array' array */
TValue *array; //数组
Node *node; // 哈希表
Node *lastfree; // 辅助寻找冲突节点的指针
struct Table *metatable;
GCObject *gclist;
} Table;
设计原理
table可以当数组也可以当哈希表,这得益于其Table结构的设计与实现。array作数组,node作哈希表。数组部分只能存放value,所以key必须是整数,其他的都放在哈希表部分。仅当哈希表没空间的时候才会触发resize,这个时候会重新调整array和node大小,数组满了并不会触发resize。
具体操作
- 查找
主要关注整型是怎么查到的。
const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TNIL:
return luaO_nilobject;
case LUA_TSTRING:
return luaH_getstr(t, rawtsvalue(key));
case LUA_TNUMBER: {
int k;
lua_Number n = nvalue(key);
lua_number2int(k, n);
// 如果double转int再转double还能相等,则认为是整数
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
return luaH_getnum(t, k); // 指向value的指针
/* else go through */
}
default: {
Node *n = mainposition(t, key);
do { /* check whether `key' is somewhere in the chain */
if (luaO_rawequalObj(key2tval(n), key))
return gval(n); /* that's it */
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
}
}
const TValue *luaH_getnum (Table *t, int key) {
/* (1 <= key && key <= t->sizearray) */
if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
return &t->array[key-1]; // 数组部分
else {
lua_Number nk = cast_num(key);
Node *n = hashnum(t, nk);
do { /* check whether `key' is somewhere in the chain */
if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
return gval(n); // 哈希部分
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
}
- 插入
同样是关注整数。
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key); // 这里get到的是指向value的指针,有可能是数组部分
t->flags = 0;
if (p != luaO_nilobject)
return cast(TValue *, p);
else {
if (ttisnil(key)) luaG_runerror(L, "table index is nil");
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
luaG_runerror(L, "table index is NaN");
return newkey(L, t, key);
}
}
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
TValue *oldval = luaH_set(L, h, key); // 这里取到value
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
setobj2t(L, oldval, val); // 将值设进去
luaC_barriert(L, h, val);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTM(L, tm, t, key, val);
return;
}
t = tm; /* else repeat with `tm' */
}
luaG_runerror(L, "loop in settable");
}
rehash:
0. t->array数组,t->node是哈希表的桶。
- 只有正整数才允许放到array中,其他key必然都放到哈希表。
- 新t->array使用率必须超过50%总table元素个数,能放多少就放多少。
- 新t->node使用率必须超过50%,装下非array的部分。
解读:
- 如果旧table当做数组来用,那么rehash之后所有元素都放在新t->array部分,新t->node指向dummynode。
- 如果旧table的key都是正整数,但是比较零散,有可能新t->array极小。
- 如果旧table的key都是正整数,但是删除了部分key,有可能新t->array比原来还小。
- 如果旧table的key大部分都是非正整数,有可能所有key都被存哈希表了,t->array为NULL。
- 如果旧table的key都是非正整数,则t->array必然是NULL。
- t->array和t->node的长度都只可能是2的幂次。
哈希表部分解决冲突:
1. 通过将key1哈希到t->node上的某个位置,称为main position(简称mp)。
2. 如果mp1空闲,则直接存在mp1。若已被key2占用,计算key2的为mp2。
3. 若mp2不等于mp1,则将key1放在mp1位置上,再为key2找个其他空闲位置。
4. 若mp2等于mp1,则再为key1重新找个位置。
方案:桶+挂链。
大致的思想:为了节省内存,链表上的节点也是桶节点。即冲突的时候,在桶里面随便找一个空节点存放,再链接起来即可。
插入:如果通过哈希算出的桶节点空闲,直接使用。
若不空闲,分两种情况:
1)这个位置上的节点和新节点具有同样的哈希值。那这个节点肯定是桶头,随便找个空节点存放新节点,挂到桶头的后面。
2)这个位置上的节点和新节点具有不同的哈希值。那这个节点肯定是属于其他链表的,帮它随便找个位置放。新节点就是这个位置的桶头。
找空闲位置:有个空闲指针,从后往前移动,空节点就是可用的。若找不到空节点,持续到t->node头,就会触发rehash。
LUA的table实现的更多相关文章
- 打印Lua的Table对象
小伙伴们再也不用为打印lua的Table对象而苦恼了, 本人曾也苦恼过,哈哈 不过今天刚完成了这个东西, 以前在网上搜过打印table的脚本,但是都感觉很不理想,于是,自己造轮子了~ 打印的效果,自己 ...
- lua的table表处理 及注意事项
lua,一款很轻量级很nice很强大的脚本语言,做为lua中使用最为频繁的table表,在使用之时还是有颇多的好处与坑的: 下面是大牛 云风的一片关于lua table的blog,可使得对lua ta ...
- lua weak table 概念解析
lua weak table 经常看到lua表中有 weak table的用法, 例如: weak_table = setmetatable({}, {__mode="v"}) 官 ...
- lua中 table 元表中元方法的重构实现
转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...
- Linux下C/C++和lua交互-Table
本来这些文章都是在我的个人网站www.zhangyi.studio,目前处在备案状态,暂时访问不了,所以搬到这边. 最近这两天需要弄清楚C++和lua间相互调用和数据传递,废话不多说,直接上过程. ...
- lua中 table 重构index/pairs元方法优化table内存占用
转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...
- lua中table的遍历,以及删除
Lua 内table遍历 在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是: 1. ipairs for index, value in ipair ...
- C调用lua的table里面的函数
网上搜索C.C++调用lua函数,有一大堆复制粘贴的. 但是搜索<C调用lua的table里面的函数> 怎么就没几个呢? 经过探索,发现其实逻辑是这样的: 1.根据name获取table ...
- lua 的 table 处理
lua 的整体效率是很高的,其中,它的 table 实现的很巧妙为这个效率贡献很大. lua 的 table 充当了数组和映射表的双重功能,所以在实现时就考虑了这些,让 table 在做数组使用时尽量 ...
- C++对Lua中table进行读取、修改和创建
C++代码: // LuaAndC.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #i ...
随机推荐
- 第三章、HTTP报文
1 报文流 HTTP 报文是在 HTTP 应用程序之间发送的数据块.这些数据块以一些文本形式的元信息(meta-information)开头.这些报文在客户端.服务器和代理之间流动.术语“流入”.“流 ...
- JDK8 新特性 Lambda表达式
1.java8中Lambda表达式基础语法: (x,y) -> {} 左侧是一个小括号,里面是要实现的抽象方法的参数,有几个参数就写几个参数名,无参可写空括号,无需声明参数类型: 中间是一个jd ...
- navicat连接远程数据库报错'client does not support authentication protocol requested by server consider ...'解决方案
[1.cmd终端连接远程mysql数据库方法] mysql -uhello -pworld -h192.168.1.88 -P3306 -Dmysql_oa mysql -u用户名 -p密码 -h ...
- asyncio之异步上下文管理器
异步上下文管理器 前面文章我们提到了上下文管理器,但是这个上下文管理器只适用于同步代码,不能用于异步代码(async def形式),不过不用担心今天我们就来讨论在异步中如何使用上下文管理器. 特别提醒 ...
- TCP/IP协议入门
TCP/IP协议入门 1. 简介 Transmission Control Protocol/Internet Protocol的简写,即传输控制协议/因特网互联协议.它是网络通信的一套协议集合. 先 ...
- 【零基础】风格迁移之deep-painterly-harmonization的安装和使用
注:原项目名叫deep-painterly-harmonization,这里我缩写下称呼其为“DPH” 注:原项目GIT链接:https://github.com/luanfujun/deep-pai ...
- 重读APUE(12)-SIGCHLD与僵尸进程
SIGCHLD信号是当子进程终止时向父进程发送的信号:它的语义如下: 如果进程明确的将该信号设置为SIG_IGN,则调用进程不会产生僵尸进程:这种情况下,wait是等不到给子进程收尸的,所以wait阻 ...
- Linux设备驱动程序 之 kmalloc
原型 kmalloc的原型如下: void *kmalloc(size_t size, gfp_t flags) 第一个参数是要分配的块的大小,第二个参数是分片标志: flags标志 最常用的标志是G ...
- pod package 生成 Framework
pod package 生成 Framework pod package 是 cocoapods 的一个插件,如果没有的话使用以下命令安装: sudo gem install cocoapods-pa ...
- mysql安装到启动遇见的问题
一.有时候安装mysql后使用mysql命令时报错 Can't connect to MySQL server on localhost (10061),或者用net start mysql 时报服务 ...