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表处理 及注意事项的更多相关文章

  1. Lua 数组排序 table.sort的注意事项

    1. table中不能有nil table.sort是排序函数,它要求要排序的目标table的必须是从1到n连续的,即中间不能有nil. 2. 重写的比较函数,两个值相等时不能return true ...

  2. 打印Lua的Table对象

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

  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 在做数组使用时尽量 ...

随机推荐

  1. html 页面内锚点定位及跳转方法总结

    第一种方法,也是最简单的方法是锚点用<a>标签,在href属性中写入DIV的id.如下: <!DOCTYPE html><html><head> < ...

  2. 通过硬编码获取dubbo服务对象

    运维进行监控dubbo服务的时候可能会调用dubbo服务对象,并且定期去执行,这时候如果需要添加新的服务,可能需要修改监控dubbo服务的配置,即dubbo-producer.xml或是dubbo-c ...

  3. KIWI Syslog配置

    日志服务器Kiwi+Syslogd+8.3.7破解版 Window收集服务器日志evtsys_exe_32 默认地,kiwi使用UDP 514端口接收日志数据,安装成功后即可接收日志 使用命令nets ...

  4. JS中可拖拽的甘特图和流程图

    甘特图: https://www.douban.com/note/441706674/ https://www.uedsc.com/jquery-ganttview.html https://gith ...

  5. ssh端口转发

    http://blog.sina.com.cn/s/blog_47094def0101fvge.html

  6. c++使用stdint.h和inttypes.h

    我们有时候需要使用int有关的宏,比如PRId64,int64_t等,就需要包含那两个头文件. 由于那两个头文件是为c99默认使用的,c++要使用它可能要定义__STDC_FORMAT_MACROS, ...

  7. 第47讲:Scala多重界定代码实战及其在Spark中的应用源码解析

    今天学习了scala的多重界定 T >: A <: B 表示T同时有下界和下界,下界为A,上界为B,A为B的子类型.下界必须写在前面,上界必须写在后面,位置不能颠倒. T<:A wi ...

  8. 查看Linux内存状况的命令及工具

    1.cat  /proc/meminfo cat  /proc/<pid>/statm  和 cat  /proc/<pid>/status  获取特定的进程的内存信息: 2. ...

  9. 错误 "sgen.exe" exited with code 1.解决方法(转)

    原文出自 http://blog.sina.com.cn/s/blog_8411d3f401015u1w.html VS中有时候编译项目会出现这样的错误: 错误   "sgen.exe&qu ...

  10. SVN分支管理策略个人见解

    本篇目录 前言 SVN分支管理策略 VisualSVN Server TortoiseSVN客户端 Repository的创建 Check out trunk创建新项目MyProject trunk更 ...