Lua的表的定义:

typedef struct Table {
CommonHeader;
lu_byte flags;
lu_byte lsizenode; /* log2 of size of `node' array */
struct Table *metatable;
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
GCObject *gclist;
int sizearray; /* size of `array' array */
} Table;

这里将Table分为了两个部分:数组部分,array指针指向数组部分的首地址,sizearray是数组的尺寸,绝大部分(注意:不是全部)正整数为key的数据都存放在数组部分;node指针指向一个hash桶,对于不能存放在数组部分的数据,都存放在hash中.如下图所示:

hash部分需要特别注意的一点是:在物理上,所有hash部分的数据,其实是存放一块连续的内存中的,即node指针指向的数组;但是从逻辑上来看,如果几块数据在同一个hash桶上,那么又是通过next指针串联起来的.

以图中的示例来分析,node数组的第一个和第三个元素,在物理上是第一和第三个元素,但是在逻辑上,它们是通过next指针串联起来的.

Table查找数据

有了以上的了解,从Table中查找一个数据的伪代码就很显而易见了:

如果输入的Key是一个正整数,并且它的值 > 0 && <= 数组大小

尝试在数组部分查找

否则尝试在Hash部分进行查找:

计算出该Key的Hash值(ltable.c中的mainposition函数),根据此Hash值访问node数组得到Hash桶所在位置

遍历该Hash桶下的所有链表元素,直到找到该Key为止

以上已经明白了Table的大致结构,来看看Table中如果新加入新的数据会怎么处理.这里有一些内容,要留到后面讲解到Lua虚拟机的时候才触及,这里先讲解一下,当新插入数据时,Table内的数组和Hash部分,做了哪些变化.

这部分中,核心的算法在ltable.c的rehash函数中,这个函数是计算当新添加数据时,数组和hash重新分配之后各自的尺寸是多少,伪代码如下:

首先分配一个位图nums,将其中的所有位置0,这个位图的意义在于:nums数组中第i个元素存放的是key在2^(i-1), 2^i之间的元素数量

遍历lua Table中的数组部分,计算在数组部分中的元素数量,更新对应的nums数组元素数量.(numusearray函数)

遍历lua Table中的Hash部分,因为其中也可能存放了正整数,也根据这里的正整数数量更新对应的nums数组元素数量.(numusehash函数)

此时nums数组已经有了当前这个Table中所有正整数的分配统计,逐个遍历nums数组,如果当前已经有的根据新的数组大小和Hash大小重新分配table的大小(computesizes函数)

这里要特别讲解的是computesizes函数,在前面的两个函数调用numusearray函数和numusehash函数之后,此时在nums位图中,已经存放了所有有关整数key的信息,即在[2^(i-1), 2^i]范围内,有多少数据.前面曾经提到过,并不是所有的正整数,都会存放在数组部分的,即使它曾经在,也有可能在之后被分配到hash部分,那么判断的依据是什么?到底怎样的数据,在重新分配之后会从数组部分挪到hash部分?

来看computesizes函数的实现:

static int computesizes (int nums[], int *narray) {
int i;
int twotoi; /* 2^i */
int a = 0; /* number of elements smaller than 2^i */
int na = 0; /* number of elements to go to array part */
int n = 0; /* optimal size for array part */
for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {
if (nums[i] > 0) {
a += nums[i];
if (a > twotoi/2) { /* more than half elements present? */
n = twotoi; /* optimal size (till now) */
na = a; /* all elements smaller than n will go to array part */
}
}
if (a == *narray) break; /* all elements already counted */
}
*narray = n;
lua_assert(*narray/2 <= na && na <= *narray);
return na;
}

注意到这样的细节:这个函数在遍历nums位图数组的时候,会将当前数据数量存放在变量a中,如果a > twotoi/2,也就是当前有一半以上的空间被利用上了,那么这部分数据会继续留在数组部分,否则就会在之后挪到hash部分了.

纯粹的使用数组或者hash表性能更高

为了证实这里的判断,简单的写一段lua代码做为实验:

function print_ipairs(t)
print("in print_ipairs")
for k, v in ipairs(t) do
print(k)
end
end function print_pairs(t)
print("in print_pairs")
for k, v in pairs(t) do
print(k)
end
end a = {}
a={1,2,3,4,5,6,7,8,9,10}
print_ipairs(a)
a[2] = nil
a[3] = nil
a[4] = nil
a[6] = nil
a["k"] = "e"
print_ipairs(a)
print_pairs(a)
输出为: in print_ipairs
1
2
3
4
5
6
7
8
9
10
in print_ipairs
1
in print_pairs
1
7
8
10
k
5
9

在这里,首先对表a赋值,有1-10共十个元素,通过调用函数print_ipairs可知,这些元素都是存放在数组部分的,这是因为ipairs取的是Table的数组部分元素.

在这之后,人为的将其中2,3,4,6元素删除,造成原来数组不满一半元素被利用上的现象,然后再插入一个新key "k",以让这个Table重新分配空间.再此之后,再次调用ipairs遍历a的数组部分,可以看到只有1被打印出来了,也就是说,在重新分配空间之后,除去已经被删除的2,3,4,6之外,只有1还在数组里面,剩下的5,7,8,9,10已经不在数组部分了.紧接着调用pairs遍历这个表,可以看出这些已经不在数组部分的值又被打印出来了,并且它们的顺序已经被打乱,不再按照数字大小顺序来排列了,它们在这次重新分配中被挪动到了hash部分.

这个实验既验证了我们前面的分析,同时也告诉我们,Table的重新分配,实际上代价是很大的,因此不建议在实际程序中,一个Table即有数组部分,也有Hash部分,纯粹一些,性能上会有提升.

lua表类型的更多相关文章

  1. Lua表(table)的用法_个人总结

    Lua表(table)的用法_个人总结 1.表的创建及表的介绍 --table 是lua的一种数据结构用来帮助我们创建不同的数据类型.如:数组和字典--lua table 使用关联型数组,你可以用任意 ...

  2. mysql高并发和表类型

    高并发:http://www.cnblogs.com/wangchaozhi/p/5061378.html 表类型:http://www.xiaoxiaozi.com/2009/07/14/1171/

  3. 浅谈MySql的存储引擎(表类型)

    来源:http://www.cnblogs.com/lina1006/archive/2011/04/29/2032894.html 什么是MySql数据库 通常意义上,数据库也就是数据的集合,具体到 ...

  4. SQLite使用(一)&&选择表类型

    在SQLite中,主要有两种表类型,带rowid的表和不带rowid的表.我们利用create table 建一张表,默认都会有一个隐含名字为rowid的主键,暂且称带rowid的表为普通表.如果建表 ...

  5. SQL 用户定义表类型,在存储过程里使用表类型,表参数作参数

    .定义表类型SUTDENTTYPE,包含三个字段,分别对应学生表的NAME,SEX和PHONE.之所以如此创建,我是准备在插入新学生数据的存储过程中,以它为参数.   GO CREATE TYPE S ...

  6. Sqlserver 自定义表类型定义,使用,删除

    --创建用户自定义表类型CREATE TYPE dbo.CustomerTable AS TABLE ( Id int NOT NULL, Name char(10) NULL, PRIMARY KE ...

  7. LUA表克隆方法归纳

    lua表克隆 将lua一个表, 克隆出一份为一个独立的另外一个表. 对于一个module, 如果在require之后,获得的表对象, 不能直接修改, 例如lua缓存此表, 但是多次逻辑执行, 都使用的 ...

  8. MySQL表类型和存储引擎版本不一致解决方法

    使用的是老版本的mysql客户端Navicate 8 ,mysql 服务端用的是mysql5.6的版本,在修改版本引擎的时候出现版本不对; mysql error ‘TYPE=MyISAM’ 解决办法 ...

  9. mysql如何修改表类型(表引擎)

    参考阅读:http://www.manongjc.com/article/1205.html 最近遇到一个修改 MySQL 表类型的问题,以前在 phpmyadmin 管理 mysql 数据库时,建立 ...

随机推荐

  1. redis介绍(2)简单安装

    我分两种方式讲解 window 下载地址:https://github.com/MSOpenTech/redis/releases. Redis 支持 32 位和 64 位.这个需要根据你系统平台的实 ...

  2. CDH5.11安装spark2.x详细步骤

    简介: 在我的CDH5.11集群中,默认安装的spark是1.6版本,这里需要将其升级为spark2.x版本.经查阅官方文档,发现spark1.6和2.x是可以并行安装的,也就是说可以不用删除默认的1 ...

  3. ASP.NET MVC 5搭建自己的视图基架 (CodeTemplate)

    我们知道,在MVC项目中添加视图时,在添加面板有模板可以选择,这里会有人疑问,这个模板位于哪里?我可以搭建自己的基架吗? 首先回答第二个问题,答案是当然可以 我这里使用的是Visual Studio ...

  4. 铁乐学Python_day08_文件操作

    一.[基本的文件操作] 参数: 1.文件路径: 2.编码方式: 3.执行动作:(打开方式)只读,只写,追加,读写,写读! #1. 打开文件,得到文件句柄并赋值给一个变量 f = open('E:/Py ...

  5. 最强自定义PHP集成环境,系统缺失dll和vc也能正常运行

    PHPWAMP支持iis.apache.nginx等web服务器,并全部支持php多版本同时运行,无限自定义mysql.php PHPWAMP支持32和64,支持自定义自动匹配系统所需dll和vc,纯 ...

  6. Virtual PC局域网共享速度慢的解决半法。转

    HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DisableTaskOffload 新建字符串,名:DisableTaskOffloa ...

  7. November 30th 2016 Week 49th Wednesday

    Your attitude, not your aptitude, will determine your altitude. 决定你人生高度的,不是你的才能,而是你的态度. Basically, I ...

  8. Php发送post请求方法

    因为自己时常用到 所以还是发布一下吧 /** * 发送post请求 * @param string $url 请求地址 * @param array $post_data post键值对数据 * @r ...

  9. 2、基于wsgiref模块DIY一个web框架

    一 web框架 Web框架(Web framework)是一种开发框架,用来支持动态网站.网络应用和网络服务的开发.这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方 ...

  10. CentOS 7下启动、关闭、重启、查看MySQL服务

    1.启动命令 [root@xufeng Desktop]# service mysqld startRedirecting to /bin/systemctl start mysqld.service ...