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 ...
随机推荐
- c++ 生成容器元素生成随机数
// random_shuffle example #include <iostream> // cout #include <algorithm> // random_shu ...
- mongodb高可用部署linux
准备三台服务器,部署方案如下: 1.安装mongodb,详细不用说: 2.创建实例分别创建目录config.configServer.shard1.shard2.shard3.logs,分别存放实例配 ...
- ICEM-水雷
原视频下载地址: https://yunpan.cn/cqhsvXAKUQEA4 访问密码 ef39
- macOS Mojave 10.14 无法安装brew缺少Command Line Tools for Xcode的解决办法
问题描述: 首先我的版本是 Xcode 10.1 如果按照以前的方法安装brew 复制 1 /usr/bin/ruby -e "$(curl -fsSL https://raw.github ...
- 重读APUE(3)-dup与文件表项
下图为调用dup之后的文件指针状态,包含如下信息: 1. dup选择了一个最小的未使用的描述符3: 2. dup(1)之后,描述符1和描述符3指向同一个文件表项: 3. dup(1)的返回值即为复制得 ...
- php学习之单例模式
<?php class Dog { private function __construct() { } //静态属性保存单例对象 static private $instance; stati ...
- SQL-W3School-基础:SQL DISTINCT 语句
ylbtech-SQL-W3School-基础:SQL DISTINCT 语句 1.返回顶部 1. 本章讲解 SELECT DISTINCT 语句. SQL SELECT DISTINCT 语句 在表 ...
- Kotlin中反射
枚举类成员 import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.memberProperties fun mai ...
- Object.keys() 遍历对象
Object.keys()方法的运用与数组遍历 Object.keys()用于获得由对象属性名组成的数组,可与数组遍历相结合使用,用起来效果杠杠滴.数组遍历可以用for()或forEach()来实现, ...
- 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_25-前后端请求响应流程小结
看图