数据结构

下面的结构体是lua中用于表示一个table,主要关注里面的arraynode

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,这个时候会重新调整arraynode大小,数组满了并不会触发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是哈希表的桶。

  1. 只有正整数才允许放到array中,其他key必然都放到哈希表。
  2. 新t->array使用率必须超过50%总table元素个数,能放多少就放多少。
  3. 新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实现的更多相关文章

  1. 打印Lua的Table对象

    小伙伴们再也不用为打印lua的Table对象而苦恼了, 本人曾也苦恼过,哈哈 不过今天刚完成了这个东西, 以前在网上搜过打印table的脚本,但是都感觉很不理想,于是,自己造轮子了~ 打印的效果,自己 ...

  2. lua的table表处理 及注意事项

    lua,一款很轻量级很nice很强大的脚本语言,做为lua中使用最为频繁的table表,在使用之时还是有颇多的好处与坑的: 下面是大牛 云风的一片关于lua table的blog,可使得对lua ta ...

  3. lua weak table 概念解析

    lua weak table 经常看到lua表中有 weak table的用法, 例如: weak_table = setmetatable({}, {__mode="v"}) 官 ...

  4. lua中 table 元表中元方法的重构实现

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  5. Linux下C/C++和lua交互-Table

    本来这些文章都是在我的个人网站www.zhangyi.studio,目前处在备案状态,暂时访问不了,所以搬到这边.  最近这两天需要弄清楚C++和lua间相互调用和数据传递,废话不多说,直接上过程. ...

  6. lua中 table 重构index/pairs元方法优化table内存占用

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  7. lua中table的遍历,以及删除

    Lua 内table遍历 在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是: 1. ipairs for index, value in ipair ...

  8. C调用lua的table里面的函数

    网上搜索C.C++调用lua函数,有一大堆复制粘贴的. 但是搜索<C调用lua的table里面的函数> 怎么就没几个呢? 经过探索,发现其实逻辑是这样的: 1.根据name获取table ...

  9. lua 的 table 处理

    lua 的整体效率是很高的,其中,它的 table 实现的很巧妙为这个效率贡献很大. lua 的 table 充当了数组和映射表的双重功能,所以在实现时就考虑了这些,让 table 在做数组使用时尽量 ...

  10. C++对Lua中table进行读取、修改和创建

    C++代码: // LuaAndC.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #i ...

随机推荐

  1. bzoj 1996

    区间 dp $f[i][j][1/0]$ 表示将理想数列的 $[i,j]$ 区间排好的方案数 $f[i][j][1]$ 表示最后进去的是第 $i$ 个人 $f[i][j][0]$ 表示最后进去的是第 ...

  2. Noip2003 提高组 神经网络

    神经网络 题目背景 人工神经网络(Artificial Neural Network)是一种新兴的具有自我学习能力的计算系统,在模式识别.函数逼近及贷款风险评估等诸多领域有广泛的应用.对神经网络的研究 ...

  3. 【概率论】2-1:条件概率(Conditional Probability)

    title: [概率论]2-1:条件概率(Conditional Probability) categories: Mathematic Probability keywords: Condition ...

  4. GoEasy实现websocket 推送消息通知到客户端

    最近在实现一个推送功能,用户扫描二维码签到,后台及时将签到成功信息推送到浏览器端.排除了前端ajax轮询的方式,决定采用websocket及时推送. 于是发现了第三方websocket推送库GoEas ...

  5. Ubuntu14.04 gzip failed file too large

    使用gzip解压一个oracle rman备份集时报错:File too large.gizp -d cosp_db_full.tar.gzgzip: cosp_db_full.tar:File to ...

  6. [题解] [SDOI2017] 序列计数

    题面 题解 和 SDOI2015 序列统计 比较像 这个无非就是把乘改成了加, NTT 改成了 MTT 再加上了一个小小的容斥 : 拿所有方案减去不合法方案即可 Code #include <a ...

  7. Linux 下基础命令

    Linux:开源 Ubuntu Centos Deepin Debian Linux mint ... 1.省钱 2.省资源 Linux由unix演化而来 Linux:开源 Unix: 闭源 sola ...

  8. CodeWar打怪升级-Python篇

    1.  The goal of this exercise is to convert a string to a new string where each character in the new ...

  9. SQL-W3School-高级:SQL FOREIGN KEY 约束

    ylbtech-SQL-W3School-高级:SQL FOREIGN KEY 约束 1.返回顶部 1. SQL FOREIGN KEY 约束 一个表中的 FOREIGN KEY 指向另一个表中的 P ...

  10. Tomcat 80端口被占用

    1.“运行”中输入cmd2.在命令行中输入netstat -ano,得到端口号对应的PID 3.打开任务管理器,点击“查看“菜单,选择“选择列”,给进程列表中添加”PID“列,然后找到PID对应的进程 ...