lua中的#是获取table中有序序列的个数。例如:
local t = {a = 1, 1,3,4,5,6}
print(#t)--5
在table的实现中,有两种方式保存元素,一种 数组,一种hash散列表。#就是获取table中的数组元素个数。例如上面代码就是获取数组的个数。其中a = 1保存在散列表中。如果在table中增加几个nil,会有什么问题呢?
local t = {a = 1, 1,3,nil,4,5,6,nil}
print(#t)--2
是不是相当诡异?为什么会这样?我们从源码一步步开剥开迷雾。
const TValue *luaH_getint (Table *t, lua_Integer key) {
/* (1 <= key && key <= t->sizearray) */
if (l_castS2U(key) - 1 < t->sizearray)
return &t->array[key - 1];
else {
Node *n = hashint(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */
if (ttisinteger(gkey(n)) && ivalue(gkey(n)) == key)
return gval(n); /* that's it */
else {
int nx = gnext(n);
if (nx == 0) break;
n += nx;
}
}
return luaO_nilobject;
}
}
static int unbound_search (Table *t, unsigned int j) {
unsigned int 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))) {//获取数组最后一个元素是否为nil,
i = j;
if (j > cast(unsigned int, MAX_INT)/2) { /*防止溢出,只有j > MAX_INT/2条件才为真。说明table是一个大的table */
/* table was built with bad purposes: resort to linear search */
i = 1;
while (!ttisnil(luaH_getint(t, i))) i++;//当数组中出现值为nil,就终止循环。
return i - 1;
}
j *= 2;
}
/* 使用二分法搜索。进过上面的while循环后,i = j, j = 原来的2倍。m = (i + j)/2 我们可以理解成m = (j + 2j)/2,第一次二分法查找,m = j + floor(j/2),此时m > j(大于table数组个数),ttisnil一定为nil,j = m.第二次二分法查找,此时i= j的,所以 m = (j + floor(j/2) + j)/2 等效于 m = j + floor(j/4),任然大于table数组的个数,ttisnil一定为nil,j = m。经过几次这样的操作,m就等于数组的长度了*/
while (j - i > 1) {
unsigned int m = (i+j)/2;
if (ttisnil(luaH_getint(t, m))) j = m;
else i = m;
}
return i;
}
int luaH_getn (Table *t) { unsigned int j = t->sizearray;//获取table中数组部分元素个数 if (j > 0 && ttisnil(&t->array[j - 1])) {//元素个数大于0且数组最后一个元素nil,就使用二分法进行查找。*上面lua代码结果诡异的原因。 /* 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->node)) /* 没有hash散列表,直接返回数组的大小 */ return j; /* that is easy... */ else return unbound_search(t, j);/*正常搜索*/ }
二分法介绍:点击我
诡异的原因知道了,就要尽量避免在table的有序数组最后插入nil值,如果二分法中间位m,恰巧m-1也是nil就会有诡异的结果。