本文来自《Lua设计与实现》的阅读笔记,推荐Lua学习者可以购买一本,深入浅出讲解lua的设计和实现原理,很赞,哈哈
 
Lua中对于表的设计,是基于数组和散列表,和其他语言不同,对于数组的下标是从1开始的,对于散列表而言,只要其键值补位nil,都可以存储在其中。
 
一、table的基本类型定义
首先看看table的数据定义,参考源码lobject.h
CommonHeader, 参看专栏前面的文章;
flags 这是一个lua的byte类型的数据,用于表示表中提供了哪些元方法,比如是否提供了元方法_index,该数据最开始设置为1,如果进行查找一次,比如_index,如果存在,这该元方法对应的flag bit设置为0,在下一次查找的时候,只需要比较这个bit即可,对应的元方法在ltm.h中
lsizenode,为散列表的大小,必定为2的幂对应的数字;
metatable,该table的元表;
array,该table的数组的指针
node, 该table的散列表的起始位置的指针;
lastfree, 该散列表的最后位置的指针
gclist, gc相关的链表
sizearray, 数组的大小,不一定为2的幂对应的数字
对于node数据,类似于其他语言中的字典设计\hash设计,就是一个键值对集合,其定义为:
需要提一下的是对于key的设计采用的是union,也就是说Lua的散列表的key,可以为nk对应的struct,也可以是TValue类型
 
二、table相关的操作的实现原理
1、查找算法的实现原理
借用原文的伪代码:
if 输入的key为整数 && key >= 0 && key <= 数组的大小
尝试在数组部分查找
else 在散列表部分查找
计算出该key的散列值,据其查找对应的node所在散列表中的位置,然后遍历其对应的链表,查找是否有该key对应的元素
举例:
local t = {}
t[1] = 0
t[100] = 0
那么1是在数组中查找,100就是在散列表中去查找了(100大于数组的len)
 
2、新增元素的实现原理
给lua中添加新元素的时候,会有可能触发重新分配table中的数组和散列表,其本质来自于散列表的rehash(由于lua对于下标超过数组的大小的数字,都会存储在散列表部分去,所以数组部分的插值不会触发rehash)
散列表的组织,就是多个mainposition,每个单独的mainposition会对应一个数据链表,当插入一个key的时候,会调用luaHset\luaH_setnum\luaH_setstr,来获得该key对应的TValue指针,如果没有,则调用内部的newkey函数来分配一个新的key:
基本的实现过程看源代码写的比较详细,这儿说一下rehash部分的操作,在ltable.c中:
1) nums中存放的是元素的数量
2)分表遍历数组(numusearray)和散列表(numusehash),统计更新nums中的数量大小
3) 重新计算数组和hash部分的大小,数组大小的计算规则:逐个遍历nums数组,获得其范围区间内所包含的整数数量大于50%的最大索引,作为rehash后的数组大小,这个索引值来自与computesizes函数:
可能看了会有点迷糊,那我就用大白话说一下吧:
首先nums数组在统计后,每个下标对应的是处于当前2^(i -1) - 2^i中的元素的个数,然后不断的累加计算,求得满足 sum > 2^n/2的最大下标值(这个下标值是nums数组中的)
所以,在不同的rehash阶段,table中的同一个key可能会在数组部分和散列表部分交替出现,也是可能的。
由于rehash会带来较大的性能消耗,所以一般都尽量避免,比如在创建表的时候,就采用预填充的算法
 
3、取长度算法的原理
如果table中元表没有重载len方法,则调用的是luaH_getn方法,其基本的伪代码为:
if 表中存在数组部分:
初始化i = 0, j = sizearray
  while(j - i > 1){
    m = (j + i)/2
  if(array[m-1] == nil)
    j = m
  else
    i = m
  }
  return i
else
  查找表中散列表长度,算法同数组部分
对于表中只有散列表的时候,其实质就是对键值为正整数的部分进行长度操作,如果既有数组,又有散列表,则优先对数组部分进行长度操作

Lua中table的实现-《Lua设计与实现》的更多相关文章

  1. lua中 table 元表中元方法的重构实现

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  2. lua中 table 重构index/pairs元方法优化table内存占用

    转载请标明出处http://www.cnblogs.com/zblade/ lua作为游戏的热更新首选的脚本,其优势不再过多的赘述.今天,我主要写一下如何重写lua中的元方法,通过自己的重写来实现对l ...

  3. lua中table的遍历,以及删除

    Lua 内table遍历 在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是: 1. ipairs for index, value in ipair ...

  4. lua中 table.getn(t) 、#t、 table.maxn(t) 这三个什么区别?

    lua中 table.getn(t) .#t. table.maxn(t) 这三个什么区别? RTlocal t = {1,888,x= 999,b=2,5,nil,6,7,[10]=1,8,{z = ...

  5. lua中table如何安全移除元素

    在Lua中,table如何安全的移除元素这点挺重要,因为如果不小心,会没有正确的移除,造成内存泄漏. 引子 比如有些朋友常常这么做,大家看有啥问题 将test表中的偶数移除掉local test = ...

  6. lua中table的常用方法

    转载:https://blog.csdn.net/Fenglele_Fans/article/details/83627021 1:table.sort() language = {"lua ...

  7. C++对Lua中table进行读取、修改和创建

    C++代码: // LuaAndC.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #i ...

  8. Lua中Table的学习

    --table 是 Lua 的一种数据结构,用来帮助我们创建不同的数据类型,如:数组.字典等 --Lua也是通过table来解决模块(module).包(package)和对象(Object)的. 例 ...

  9. lua中遍历table的几种方式比较

    当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是: for key, value in pairs(tbtes ...

随机推荐

  1. python文件读read()、readline()、readlines()对比

    读取文件的三个方法:read().readline().readlines().均可接受一个变量用以限制每次读取的数据量,但通常不使用.本章目的是分析和总结三种读取方式的使用方法和特点. 一.read ...

  2. js中, 用变量或对象作为if或其他条件的表达式

    源: 因为js是弱语言, 就体现在js的变量是弱类型的, 在js中所有变量类型声明都用var, 而在其他强类型语言中,如java/c,必须有强制类型转换和类型检查才能编译通过等, 但是: 弱语言也有优 ...

  3. Mycat 分片规则详解--ER关系表分片

    ER模型是实体关系模型,基本元素是实体.关系和属性,Mycat 针对ER关系表的切分规则中,使得有相互依赖的表能够按照某一个规则切分到相同的节点上,避免垮库 Join 关系查询,下面的示例为订单(or ...

  4. JAVA基本数据类型和引用数据类型的区别

    [基本数据类型] 基本数据类型:声明时直接在栈内存中开辟空间,并直接在当前内存中存放数据,赋值时传递的是变量中的值,总的来说,基本数据类型是传值的. [引用数据类型] 声明引用数据类型(数组或对象), ...

  5. STL --> stack栈

    stack栈 c++stack(堆栈)是一个容器的改编,它实现了一个先进后出的数据结构(FILO),使用该容器时需要包含#include<stack>头文件: 定义stack对象示例: s ...

  6. android中与SQLite数据库相关的类

    为什么要在应用程序中使用数据库?数据库最主要的用途就是作为数据的存储容器,另外,由于可以很方便的将应用程序中的数据结构(比如C语言中的结构体)转化成数据库的表,这样我们就可以通过操作数据库来替代写一堆 ...

  7. Spring基于注解开发异常

    基于注解开发: 一开始:用的jar包: 百度查到: 导入aop包: 没用 有的说: Spring版本和jdk版本不匹配 于是我换成了4.0版本 导入的jar包: 还是报错. 解决办法:添加spring ...

  8. C#简单入门

    公司给的一个小的practice C# vs2017 Stage 1 (cmd)1. Parse the dll (reflection)2. Write all the public methods ...

  9. Beta Scrum Day 1

    听说

  10. Beta冲刺NO.2

    Beta冲刺 第二天 1.昨天的问题 由于前面的冲刺留下的问题很多,而且混乱的代码给我们接下来的完善工作带来了巨大的困难. 2. 今天解决的进度 潘伟靖: 1.对代码进行了review 2.为系统增加 ...