四种方式遍历table
- for泛型遍历:pairs
for key, value in pairs(tbtest) do
XXX
end
-
for泛型遍历:ipairs
for key, value in ipairs(tbtest) do
XXX
end -
for数值遍历 #(tbtest)
for i=1, #(tbtest) do
XXX
end -
for数值 table.maxn(tbtest)
for i=1, table.maxn(tbtest) do
XXX
end
首先要明确一点,就是lua中table并非像是C/C++中的数组一样是顺序存储的,准确来说lua中的table更加像是C++中的map,通过Key对应存储Value,但是并非顺序来保存key-value对,而是使用了hash的方式,这样能够更加快速的访问key对应的value,我们也知道hash表的遍历需要使用所谓的迭代器来进行,同样,lua也有自己的迭代器,pairs和ipairs遍历就是使用迭代器遍历。
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4,
}
for key, value in pairs(tbtest) do
print(value)
end
我认为输出应该是1,2,3,4,实际上的输出是1,2,4,3。我因为这个造成了一个bug,这是后话。
也就是说for k,v in pairs(tbtest) do 这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。
当然,同时lua也提供了按照key的大小顺序来遍历的,注意,是大小顺序,仍然不是key定义的顺序,这种遍历方式就是for k,v in ipairs(tbtest) do。
for k,v in ipairs(tbtest) do 这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[5] = 5,
}
for k,v in ipairs(tbtest) do
print(v)
end
只会打印1,2,3。而5则不会显示。
local tbtest = {
[2] = 2,
[3] = 3,
[5] = 5,
}
for k,v in ipairs(tbtest) do
print(v)
end
这样就一个都不会打印。
第三种遍历方式有一种神奇的符号'#',这个符号的作用是是获取table的长度,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
}
print(#(tbtest))
打印的就是3
tbtest = {
[1] = 1,
[2] = 2,
[6] = 6,
}
print(#(tbtest))
这样打印的就是2,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,‘#’都会查找key为1的值开始。
如果table的定义是这样的:
tbtest = {
["a"] = 1,
[2] = 2,
[3] = 3,
}
print(#(tbtest))
那么打印的就是0了。因为‘#’没有找到key为1的值。
同样:
tbtest = {
[“a”] = 1,
[“b”] = 2,
[“c”] = 3,
}
print(#(tbtest))
打印的也是0
所以,for i=1, #(tbtest) do这种遍历,只能遍历当tbtest中存在key为1的value时才会出现结果,而且是按照key从1开始依次递增1的顺序来遍历,找到一个递增不是1的时候就结束不再遍历,无论后面是否仍然是顺序的key,比如:
table.maxn获取的只针对整数的key,字符串的key是没办法获取到的,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
}
print(table.maxn(tbtest))
tbtest = {
[6] = 6,
[1] = 1,
[2] = 2,
}
print(table.maxn(tbtest))
这样打印的就是3和6,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,table.maxn都会获取整数型key中的最大值。
如果table的定义是这样的:
tbtest = {
["a"] = 1,
[2] = 2,
[3] = 3,
}
print(table.maxn(tbtest))
那么打印的就是3了。如果table是:
tbtest = {
[“a”] = 1,
[“b”] = 2,
[“c”] = 3,
}
print(table.maxn(tbtest))
print(#(tbtest))
那么打印的就全部是0了。
换句话说,事实上因为lua中table的构造表达式非常灵活,在同一个table中,你可以随意定义各种你想要的内容,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
["a"] = 4,
["b"] = 5,
}
同时由于这个灵活性,你也没有办法获取整个table的长度,其实在coding的过程中,你会发现,你真正想要获取整个table长度的地方几乎没有,你总能采取一种非常巧妙的定义方式,把这种需要获取整个table长度的操作避免掉,比如:
tbtest = {
tbaaa = {
[1] = 1,
[2] = 2,
[3] = 3,
},
["a"] = 4,
["b"] = 5,
}
你可能会惊讶,上面这种table该如何遍历呢?
for k, v in pairs(tbtest) do
print(k, v)
end
输出是:a 4 b 5 tbaaa table:XXXXX。
由此你可以看到,其实在table中定义一个table,这个table的名字就是key,对应的内容其实是table的地址。
当然,如果你用
for k, v in ipairs(tbtest) do
print(k,v)
end
来遍历的话,就什么都不会打印,因为没有key为1的值。但当你增加一个key为1的值时,ipairs只会打印那一个值,现在你明白ipairs是如何工作的吧。
for i=1, #tbtest do --这种方式无法遍历所有的元素,因为'#'只会获取tbtest中从key为1开始的key连续的那几个元素,如果没有key为1,那么这个循环将无法进入
for i=1, table.maxn(tbtest) do --这种方式同样无法遍历所有的元素,因为table.maxn只会获取key为整数中最大的那个数,遍历的元素其实是查找tbtest[1]~tbtest[整数key中最大值],所以,对于string做key的元素不会去查找,而且这么查找的效率低下,因为如果你整数key中定义的最大的key是10000,然而10000以下的key没有几个,那么这么遍历会浪费很多时间,因为会从1开始直到10000每一个元素都会查找一遍,实际上大多数元素都是不存在的。
for k, v in pairs(tbtest) do
这个是唯一一种可以保证遍历tbtest中每一个元素的方式,别高兴的太早,这种遍历也有它自身的缺点,就是遍历的顺序不是按照tbtest定义的顺序来遍历的,这个前面讲到过,当然,对于不需要顺序遍历的用法,这个是唯一可靠的遍历方式。
for k, v in ipairs(tbtest) do
这个只会遍历tbtest中key为整数,而且必须从1开始的那些连续元素,如果没有1开始的key,那么这个遍历是无效的
网友评论