美文网首页
3、表的遍历?

3、表的遍历?

作者: GameObjectLgy | 来源:发表于2020-10-20 01:12 被阅读0次
    四种方式遍历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,那么这个遍历是无效的

    相关文章

      网友评论

          本文标题:3、表的遍历?

          本文链接:https://www.haomeiwen.com/subject/oztsjktx.html