lua的table表处理 及注意事项
lua,一款很轻量级很nice很强大的脚本语言,做为lua中使用最为频繁的table表,在使用之时还是有颇多的好处与坑的;
下面是大牛 云风的一片关于lua table的blog,可使得对lua table内在机制 窥测一二;
lua 的整体效率是很高的,其中,它的 table 实现的很巧妙为这个效率贡献很大。
lua 的 table 充当了数组和映射表的双重功能,所以在实现时就考虑了这些,让 table 在做数组使用时尽量少效率惩罚。
lua 是这样做的。它把一个 table 分成数组段和 hash 段两个部分。数字 key 一般放在数组段中,没有初始化过的 key 值全部设置为 nil 。当数字 key 过于离散的时候,部分较大的数字 key 会被移到 hash段中去。这个分割线是以数组段的利用率不低于 50% 为准。 0 和 负数做 key 时是肯定放在 hash 段中的。
string 和 number 都放在一起做 hash ,分别有各自的算法,但是 hash 的结果都在一个数值段中。hash 段采用闭散列方法,即,所有的值都存在于表中。如果hash 发生碰撞,额外的数据记在空闲槽位里,而不额外分配空间存放。当整个个表放满后,hash 段会扩大,所有段内的数据将被重新 hash ,重新 hash 后,冲突将大大减少。
这种 table 的实现策略,首先保证的是查找效率。对于把 table 当数组使用时将和 C 数组一样高效。对于 hash 段的值,查找几乎就是计算 hash 值的过程(其中string 的 hash 值是事先计算好保存的),只有在碰撞的时候才会有少许的额外查找时间,而空间也不至于过于浪费。在 hash 表比较满时,插入较容易发生碰撞,这个时候,则需要在表中找到空的插槽。lua 在table 的结构中记录了一个指针顺次从一头向另一头循序插入来解决空槽的检索。每个槽点在记录 next 指针保存被碰撞的 key 的关联性。
整个来说,这种解决方法是非常不错的。
关于映射表的实现,我前段时间也做过一个别的研究。贴在留言本上:
<a href="http://www.codingnow.com/2004/board/view.php?paster=777&reply=0">树表结合的一种映射表实现</a>
<a href="http://www.codingnow.com/2004/board/view.php?paster=776&reply=0">在 vector , map , list 间取得平衡</a>
原文链接: http://blog.codingnow.com/2005/10/lua_table.html
即便作为lua 开发蛮久的coder来讲,很多东西不亲自去考究一下,还不是很清晰的,不如lua table的长度问题就是一个 很奇葩的例子;下面的这些也许你就不是很清楚了;
想要取得lua table长度,有这么几种方法,table.getn(table_name), #table_name, table.maxn(table_name), 再加上 ipairs(table_name) 和pairs(table_name)遍历;
根据云风所写的文章可以得知,lua的两种不同的存储方式,自然的,上述的几种 取得lua table 长度的几种方式也 存在区别;
table.maxn(table)
table.maxn()函数返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.
table.getn(table)
返回table中元素的个数
#(table)
返回的是lua table 中key为连续整型数字(抑或是 默认整型)的长度数;
pairs()函数基本和ipairs()函数用法相同, 区别在于pairs()可以遍历整个table, 即包括数组及非数组部分.
关于对lua table长度的问题,http://blog.csdn.net/dssdss123/article/details/12676329 对于一些奇葩问题的讲述还是有些深入的;亲测【lua version=5.1.4】,的确如此;
但是还是有些东西需要补充的,对于 #用法,其表现和table.getn()在很多极端的情况下都是类似的;table.maxn(),因为获取的是table中所有正数key值中最大的key值.可以不连续;
local tblTest =
{
"this 1",
"this 2",
[] = ,
[] = ,
[] = ,
"this 3",
[] = ,
}
print(table.getn(tblTest))
print(#(tblTest))
print(table.maxn(tblTest)) --===========================
>lua -e "io.stdout:setvbuf 'no'" "filedeal.lua" --=========================== local tblTest =
{
"this 1",
[] = ,
[] = ,
[] = ,
"this ",
--"adsfasd",
[] = ,
}
print(table.getn(tblTest))
print(#(tblTest))
print(table.maxn(tblTest))
--===========================
>lua -e "io.stdout:setvbuf 'no'" "filedeal.lua" --=========================== local tblTest =
{
"this 1",
[] = ,
[] = ,
[] = ,
--"adsfasd",
[] = ,
}
print(table.getn(tblTest))
print(#(tblTest))
print(table.maxn(tblTest))
--===========================
>lua -e "io.stdout:setvbuf 'no'" "filedeal.lua" --===========================
上述三个tabTest 的不同在于 这都是lua默认的下表是从1开始,有两个的默认的,使得 [3]=2,这项元素足以将连续性接上,当接不上的时候,因为不连续行,自然打印值有所不同了;
倘若在后面或者该表 其中再坠入一个nil,所输出来的内容又是不一样的:所以~不要在lua的table中使用nil值,如果一个元素要删除,直接remove,不要用nil去代替。
倘若再将元素后面加一项 下表默认的元素,其结果会怎样呢? 例 如下代码:
local tblTest =
{
"this 1",
[] = ,
[] = ,
[] = ,
"this 3",
--"adsfasd",
[] = ,
}
for k , v in ipairs(tblTest) do
print(k,v)
end
--=============================
this
this --============================= local tblTest =
{
"this 1",
[] = ,
[] = ,
[] = ,
"this 3",
"this 4",
--"adsfasd",
[] = ,
}
for k , v in ipairs(tblTest) do
print(k,v)
end --=============================
this
this
this --=============================
由此可见,lua 默认的下表值会将显示的覆盖,即便是 再调整下顺序 也是如是,至于为何如此,不理解,有待参悟【欢求 大神指正】;
这样的意外在lua中 挺多的,只要明白了 基本的原理,倒也不足为奇了;
至于lua table的遍历 可参见 http://rangercyh.blog.51cto.com/1444712/1032925 这这篇文章;讲的挺详细的;
另外: 关于lua table的其他小问题:
1, 配置lua table 元素之间,以”,“ 或者”;“完全是一样的【可参见lua手册】,看你的爱好了,推荐的是:用分好可以作为 元素类型的不同而分割开显示下;
2,不要在lua的table中使用nil值,如果一个元素要删除,直接remove,不要用nil去代替。
3,判断lua table 是否为nil 不能用 if a == {} then 【错误的】(这样的结果就是a == {}永远返回false,是一个逻辑错误。因为这里比较的是table a和一个匿名table的内存地址。);
if table.maxn(a) == 0 then 【错误的】这样做不保险啊,除非table的key都是数字,而没有hash部分。
if #(a) == 0 then 也是不靠谱的,除非你能保证没人这样写这个table like this:tab = {nil,1,nil;} 用#tab print出来 的确是0, 能说此tab是nil的?
可以使用lua内置的next来判断; if next(a) == 0 then ;
4,应该尽量使用 local 变量而非 global 变量。这是 Lua 初学者最容易犯的错误。 global 变量实际上是放在一张全局的 table 里的。 global 变量实际上是利用一个 string (变量名作 key) 去访问这个 table 。 虽然[InterWiki]Lua5 的 table 效率很高 ,但是相对于 local 变量,依然有很大的效率损失。 local 变量是直接通过 Lua 的堆栈访问的。有些 global 变量的访问是不经意的,比如双重for中使用的pairs或者ipirs(全局函数),如果在使用循环外层 local pairs=pairs会对性能有些不同层次的提升;
5, 警惕临时变量 字符串的连接操作,会产生新的对象。这是由 lua 本身的 string 管理机制导致的。lua 在 VM 内对相同的 string 永远只保留一份唯一 copy ,这样,所有字符串比较就可以简化为地址比较。这也是 lua 的 table 工作很快的原因之一。这种 string 管理的策略,跟 java 等一样,所以跟 java 一样,应该尽量避免在循环内不断的连接字符串,比如 a = a..x 这样。每次运行,都很可能会生成一份新的 copy 。
6, 同样,记住,每次构造一份 table 都会多一份 table 的 copy 。比如在 lua 里,把平面坐标封装成 { x, y } 用于参数传递,就需要考虑这个问题。每次你想构造一个坐标对象传递给一个函数,{ 10,20 } 这样明确的写出,都会构造一个新的 table 出来。要么,我们想办法考虑 table 的重用;要么,干脆用 x,y 两个参数传递坐标。 同样需要注意的是以 function foo (...) 这种方式定义函数, ... 这种不定参数,每次调用的时候都会被定义出一个 table 存放不定数量的参数。 这些临时构造的对象往往要到 gc 的时候才被回收,过于频繁的 gc 有时候正是效率瓶颈。
7,【未完待续...】
lua的table表处理 及注意事项的更多相关文章
- Lua 数组排序 table.sort的注意事项
1. table中不能有nil table.sort是排序函数,它要求要排序的目标table的必须是从1到n连续的,即中间不能有nil. 2. 重写的比较函数,两个值相等时不能return true ...
- 打印Lua的Table对象
小伙伴们再也不用为打印lua的Table对象而苦恼了, 本人曾也苦恼过,哈哈 不过今天刚完成了这个东西, 以前在网上搜过打印table的脚本,但是都感觉很不理想,于是,自己造轮子了~ 打印的效果,自己 ...
- 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 在做数组使用时尽量 ...
随机推荐
- H5中的拖拽事件
最近浏览了张鑫旭大神的基于HTML5 drag/drop模块拖动插入排序删除完整实例,感觉受益匪浅.于是将最做的demo记录下来. 首先浏览一下事件,这些事件比较好记,只要记住用在谁的身上就好了,无非 ...
- Selenium IDE和Selenium RC的安装
1 安装FireBug和FirePath 1.在火狐浏览器中,点击”添加附件”按钮,弹出”附加组件管理器”页面 2.在弹出页面中,输入“fireBug”,点击“搜索”按钮,弹出fireBu ...
- 解决Android5.0以后DatePicker选择时间无效的bug。
一.在布局中加上这句话. 加上了这句话后,就相当于强制用5.0以前的外观,所以外观会有所变化: 5.0以上没有这句话的外观: 加上之后的外观: 二.可以用DatePickerDialog代替
- 电子科技大学第八届ACM趣味程序设计竞赛第四场(正式赛)题解
A. Picking&Dancing 有一列n个石子,两人交替取石子,每次只能取连续的两个,取走后,剩下的石子仍然排成1列.问最后剩下的石子数量是奇数还是偶数. 读懂题意就没什么好说的. #i ...
- 【九度OJ】题目1061:成绩排序
题目描述: 有N个学生的数据,将学生数据按成绩高低排序,如果成绩相同则按姓名字符的字母序排序,如果姓名的字母序也相同则按照学生的年龄排序,并输出N个学生排序后的信息. 输入: 测试数据有多组,每组输入 ...
- JAVA设计模式--工厂方法模式
工厂方法设计模式 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关.是具体工厂角色必须实现的接口或者必须继承的父类.在java中它由抽象类或者接口来实现.具体工厂角色:它含有和具体业务逻辑有关 ...
- xml文档解析
XML文档解主要分为四种解析方式,官方提供的两种分别是:DOM 和 SAX,第三方分别是:JDOM 和 DOM4j 测试用的xml文档: <?xml version="1.0" ...
- selenium support
org.openqa.selenium.support.ui.Select select = new org.openqa.selenium.support.ui.Select(driver.fi ...
- python easy_install pip django
1. install python (2.7.8) 2. set PATH, add python27 3. python easy_install.py 4. easy_install pip 5. ...
- Linux磁盘管理
本系列的博客来自于:http://www.92csz.com/study/linux/ 在此,感谢原作者提供的入门知识 这个系列的博客的目的在于将比较常用的liunx命令从作者的文章中摘录下来,供自己 ...