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. mybatis大于小于的转义

    最近在使用mybatis,然后用到了小于等于,直接在XML中使用了<=,结果XML文件一直显示红色错误,如下: sum(case when p.pool_year <= '2014' th ...

  2. bootstrap作为mixin库的应用模式

    Bootstrap作为一个非常流行的前端css框架,得到了非常多的应用.一般的使用方法都是直接download bootstrap.css,作为css文件引入到html的markup中,随后直接引用其 ...

  3. Oracle EBS AP 供应商地点失效

    /* 供应商地点失效 creation: created by jenrry 20161108 1.00 */ DECLARE lv_return_status ) := NULL; ln_msg_c ...

  4. “拒绝了对对象数据库的 EXECUTE 权限”之解决

    “拒绝了对对象'aspnet_CheckSchemaVersion'的 EXECUTE 权限”之解决 [错误状态] “/XXX”应用程序中的服务器错误. ----------------------- ...

  5. Oracle GoldenGate DDL 详细说明 使用手册(较早资料)

    一. 概述 DDL 相关的参数包括:DDL.DDLERROR.DDLOPTIONS.DDLSUBST.DDLTABLE.GGSCHEMA. PURGEDDLHISTORY.PURGEMARKERHIS ...

  6. CRM总结

    一.CRM初始 CRM,客户关系管理系统(Customer Relationship Management).企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销 ...

  7. Vue的路由

    Vue可以实现一种类是ajax不刷新但是切换界面 然后 只是在你的url中的当前地址后面追加信息 首先你要先当如这个路由的模块: <script src="https://unpkg. ...

  8. pycharm something

    e1:使用的版本2016.3 发现无法执行py程序,执行菜单为灰色.选择py为2.7. e2:使用过jython可以运行,但是语法有问题str.formate无法执行

  9. iOS设计模式 - 组合

    iOS设计模式 - 组合 原理图 说明 将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性.掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ...

  10. Composer 的简介、安装及使用

    Composer的简介 简单说,Composer 就是一个安装包管理工具,服务于 PHP 生态系统.它包括了两个部分:Composer 和 Packagist. Composer Composer 是 ...