在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Lua设计里面,Table是一个特别关键的部分。它可以表示很多的数据结构,可以是Array,可以是Map,可以根据自己的需要实现栈,队列等等,使用起来方便。源码里面的设计显得特别重要了,它是被很频繁地使用,提高性能是设计者重中之中。 首先先看一下Lua的总体设计:它分为两部分,分别是数组Array和Hash部分。数组部分主要是存储下标从1开始的连续不为空的节点内容,如果是中间断开部分会存到hash部分。Hash部分是存储各种类型的离散数据。如下图
1.对于一个Table初始化的时候,如果是空表,即Array和Hash部分长度都为0;下面着重看Hash部分
2.现在我们要给Table赋值,例如执行:tb["k0"] = "v0", key为"k0", value为:"v0" 3.现在赋值: tb["k1"] = "v1",通过key查找mainposition的时候,回到刚上一步newkey的过程。此时假如查找"k1"的mainposition和"k0"的一样时候,这时"k1"发现自己的mainposition被占用了。既然被占用了,那我总得找个地方安放吧?好,那我去找一个空的坑填一下。怎么找?通过lastfree从后往前找,一个一个找。根据此时内存结构,应该找的到下标为2的节点,同时lastfree会停留在这里。既然"k0"占了"k1"的位置,"k1"要想办法协调了。要检查"k0"的mainposition到底是不是现在的位置。如果是的话,那就是"兄弟了"(两个key通过hash计算出来的mainposition一样),如果不是就需要让位了。如果是"兄弟",那就将就下,但是要通过链表连接起,表明一下“兄弟”关系。把"k1"指向"k0"的下一个节点,再把“k0”的next指向“k1”,此时内存结构图为: 4.再赋值多一次(我也不想啊,一定要操作这么多次才能把情况说完),tb["k2"] = "v2"。目前的情况是:k0是在自己的mainposition上面,k1不是在自己的mainposition(这个位置不属于k1自己的小天地)。此时,“k2”来搞事了,它计算得出来,“k1”所在的节点是"k2"的mainposition。既然不是“兄弟”,那就把属于自己的东西拿回来啊。拿归拿,先找一个freepos来谈判。通过lastfree继续向前找,找到下标为1的节点,lastfree此时停留在1的位置。好了,找到位置了,然后呢,让"k1"自己搬过去空位置去,同时让“k0”的下一个继续指向“k1”所在的位置,这样子,把"k1"的mainposition腾出来了,大家都乐得其所。此时内存结构图为: 5.再来再来,还没完。假设现在赋值, tb["k3"] = "v3",刚好"k3"毫无差池地落入了第0个里,好了,刚好填空坑了。 6.你以为这样就完了吗?怎么可能呢。这个时候,再来一个 tb["k4"] = "v4"。噢,找不位置了,一个位置都没有了。怎么办?扩容啊,麻烦给我扩大点。扩大的规则是:根据当前lsizenode来增加,当前指数为2,2的2次方,为4。在这个基础上指数增加一级,就是lsizenode即为3了,变成2的3次,扩容后数量为8,亦即为rehash。rehash的过程是:先分配一块新的内存块,像这里的情况是分配了长度为8的节点大小内存块。此时,要把旧的迁移过来新内存块。但是,它并不是按顺序一个一个对位拷贝的,遍历每一个节点的时候,需要重新去找一次mainposition,所以分配前后的位置可能会不一样(它和Array部分不一样,Array部分是直接一对一,位置不变地拷贝的)。这个时候有rehash之后可能是(注意!这仅仅是可能,只是要表达,它的位置需要重新找): rehash以后,再重新去找"k4"的位置,重复第3步的逻辑。
整个Table的hash部分是最为关键核心的,需要理清楚实际的内部原理。 |
请发表评论