lua中#取table长度的一些坑以及如何改良
【前言】
废话不多说,直接看个例子:
local tab = {a = 1, 2,3,4}
local tab1 = {1,2}
print(#tab,#tab1)-->3 2
这个print输出结果中#tab=3,明明有4个元素,取的长度只等于3,这是我在工作中遇到的bug,接下来会分析源码,并将改良方法一并奉上。
【#底层源码分析】
#代表取长度。对字符串来说,取字符串的长度, 对table来说取长度会复杂一些。
lua中的table可以用数字或字符串等作为key(例[“str”]), #号返回在表’t’中最开始的连续部分的整数索引(即i为整数,t[i]非nil,且t[i+1]为nil),如果t[1]为nil则为0,即使t[5], t[6], t[7]是存在的,#t仍然为零。
可能看代码更容易理解。
/*
** Try to find a boundary in table 't'. A 'boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
lua_Unsigned luaH_getn (Table *t) {
unsigned int j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) {
/* there is a boundary in the array part: (binary) search for it */
unsigned int i = 0;
while (j - i > 1) {
unsigned int m = (i+j)/2;
if (ttisnil(&t->array[m - 1])) j = m;
else i = m;
}
return i;
}
/* else must find a boundary in hash part */
else if (isdummy(t)) /* hash part is empty? */
return j; /* that is easy... */
else return unbound_search(t, j);
}
这个是lua5.3中求table长度的方法, 函数中t->array是一个数组,用二分法找到一个索引i,使得t[i]存在而t[i+1]为nil,就把i作为table的长度返回。
luaH_getn方法先检查先检查数组边界,数组没有就检查hash部分的边界,也就是上面源码中,会先遍历数组部分,数组部分有就结束,没有再遍历hash表部分,再来看下关于hash表部分的取长度。
static lua_Unsigned unbound_search (Table *t, lua_Unsigned j) {
lua_Unsigned i = j; /* i is zero or a present index */
j++;
/* find 'i' and 'j' such that i is present and j is not */
while (!ttisnil(luaH_getint(t, j))) {
i = j;
if (j > l_castS2U(LUA_MAXINTEGER) / 2) { /* overflow? */
/* table was built with bad purposes: resort to linear search */
i = 1;
while (!ttisnil(luaH_getint(t, i))) i++;
return i - 1;
}
j *= 2;
}
/* now do a binary search between them */
while (j - i > 1) {
lua_Unsigned m = (i+j)/2;
if (ttisnil(luaH_getint(t, m))) j = m;
else i = m;
}
return i;
}
所以lua中取table的长度并不表示元素的数量,只有可以肯定table中的元素是用从1开始的连续的整数作为key时才可以得到正确的结果。比如创建一个列表时, 通过 t[#t +1] = new_node 在结尾添加一个元素,下面我们将用pairs遍历表的方式去得到table的长度。
【#对table取长度换成pairs遍历表】
function GetTalbleMapLen(tab)
local len = 0
for _, v in pairs(tab) do
if v then len = len+1 end
end
return len
end
local tab = {a = 1, 2,3,4}
print(GetTalbleMapLen(tab))-->4