lua表类型
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表类型的更多相关文章
- Lua表(table)的用法_个人总结
Lua表(table)的用法_个人总结 1.表的创建及表的介绍 --table 是lua的一种数据结构用来帮助我们创建不同的数据类型.如:数组和字典--lua table 使用关联型数组,你可以用任意 ...
- mysql高并发和表类型
高并发:http://www.cnblogs.com/wangchaozhi/p/5061378.html 表类型:http://www.xiaoxiaozi.com/2009/07/14/1171/
- 浅谈MySql的存储引擎(表类型)
来源:http://www.cnblogs.com/lina1006/archive/2011/04/29/2032894.html 什么是MySql数据库 通常意义上,数据库也就是数据的集合,具体到 ...
- SQLite使用(一)&&选择表类型
在SQLite中,主要有两种表类型,带rowid的表和不带rowid的表.我们利用create table 建一张表,默认都会有一个隐含名字为rowid的主键,暂且称带rowid的表为普通表.如果建表 ...
- SQL 用户定义表类型,在存储过程里使用表类型,表参数作参数
.定义表类型SUTDENTTYPE,包含三个字段,分别对应学生表的NAME,SEX和PHONE.之所以如此创建,我是准备在插入新学生数据的存储过程中,以它为参数. GO CREATE TYPE S ...
- Sqlserver 自定义表类型定义,使用,删除
--创建用户自定义表类型CREATE TYPE dbo.CustomerTable AS TABLE ( Id int NOT NULL, Name char(10) NULL, PRIMARY KE ...
- LUA表克隆方法归纳
lua表克隆 将lua一个表, 克隆出一份为一个独立的另外一个表. 对于一个module, 如果在require之后,获得的表对象, 不能直接修改, 例如lua缓存此表, 但是多次逻辑执行, 都使用的 ...
- MySQL表类型和存储引擎版本不一致解决方法
使用的是老版本的mysql客户端Navicate 8 ,mysql 服务端用的是mysql5.6的版本,在修改版本引擎的时候出现版本不对; mysql error ‘TYPE=MyISAM’ 解决办法 ...
- mysql如何修改表类型(表引擎)
参考阅读:http://www.manongjc.com/article/1205.html 最近遇到一个修改 MySQL 表类型的问题,以前在 phpmyadmin 管理 mysql 数据库时,建立 ...
随机推荐
- git常用命令简集
基础操作: 初始化git仓库: git init 提交到暂存区: git add “filename” 提交到分支: git commit -m "注释" 工作区状态: git s ...
- Mysql中的delimiter详解
初学mysql时,可能不太明白delimiter的真正用途,delimiter在mysql很多地方出现,比如存储过程.触发器.函数等. 学过oracle的人,再来学mysql就会感到很奇怪,百思不得其 ...
- ASM Disk Discovery 最佳实践
ASM DISK 的Discovery PATH ASM实例的ASM_DISKSTRING初始化参数使用一个逗号分割的字符串限制ASM实例发现的DISK可以用于ASM DISK, 该字符串支持通配符如 ...
- MySQL: OPTIMIZE TABLE: Table does not support optimize, doing recreate + analyze instead
show create table history;-------------------------- CREATE TABLE `foo` ( `itemid` bigint(20) unsig ...
- 如何让触摸事件穿透一个View
如何让触摸事件穿透一个View 偶然间发现,如何屏蔽或者让触摸事件穿透一个view是一个很简单的事情. 现象: 源码: // // ViewController.m // UserInteractio ...
- 《C++ Primer Plus》读书笔记之九—使用类
第十一章 使用类 1.操作符函数的格式:operator op(argument-list).op是将要重载的操作符. 2.操作符重载函数的两种调用方式:①函数表示法:C=A.operator+(B) ...
- 铁乐学Python_day06-整数和字符串小数据池
python小数据池(内存地址) 今天来学习认识一下python中的小数据池. 我们都知道 ==是用来作比较,要是两个变量的数值相等, 用==比较返回的bool值会是True: a = 1000 b ...
- linux之安装nali本地解析IP归属
参考博文:http://www.dwhd.org/20150802_014526.html 1.安装nali wget http://www.dwhd.org/wp-content/uploads/2 ...
- 微信小程序websocket
微信小程序websocket 微信小程序带有websocket可以提供使用,但是官方文档写的东西很少,而且小程序后台能力弱这一点也是十分的坑爹,这就导致了socket长连接一切后台就会出现断开的情况, ...
- 寒假短期学习计划 - C++
寒假短期学习计划 - C++ 一.所选课程 && 相关 0.选以下课的理由: 选课理由0: 只是短期的计划,先选些短视频感受:之后再视情况选其他课: 选课理由1: 难度低,以前自学过一 ...