数据结构

下面的结构体是lua中用于表示一个table,主要关注里面的array和node。

typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */ lu_byte lsizenode; /* log2 of size of 'node' array */
unsigned int alimit; /* "limit" of 'array' array */
TValue *array; //数组 Node *node; // 哈希表
Node *lastfree; // 辅助寻找冲突节点的指针 struct Table *metatable;
GCObject *gclist;
} Table;

设计原理

table可以当数组也可以当哈希表,这得益于其Table结构的设计与实现。array作数组,node作哈希表。数组部分只能存放value,所以key必须是整数,其他的都放在哈希表部分。仅当哈希表没空间的时候才会触发resize,这个时候会重新调整array和node大小,数组满了并不会触发resize。

具体操作

  • 查找

主要关注整型是怎么查到的。

const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TNIL:
return luaO_nilobject;
case LUA_TSTRING:
return luaH_getstr(t, rawtsvalue(key));
case LUA_TNUMBER: {
int k;
lua_Number n = nvalue(key);
lua_number2int(k, n);
// 如果double转int再转double还能相等,则认为是整数
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
return luaH_getnum(t, k); // 指向value的指针
/* else go through */
}
default: {
Node *n = mainposition(t, key);
do { /* check whether `key' is somewhere in the chain */
if (luaO_rawequalObj(key2tval(n), key))
return gval(n); /* that's it */
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
}
}
const TValue *luaH_getnum (Table *t, int key) {
/* (1 <= key && key <= t->sizearray) */
if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
return &t->array[key-1]; // 数组部分
else {
lua_Number nk = cast_num(key);
Node *n = hashnum(t, nk);
do { /* check whether `key' is somewhere in the chain */
if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
return gval(n); // 哈希部分
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
}
  • 插入

同样是关注整数。

TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key); // 这里get到的是指向value的指针,有可能是数组部分
t->flags = 0;
if (p != luaO_nilobject)
return cast(TValue *, p);
else {
if (ttisnil(key)) luaG_runerror(L, "table index is nil");
else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
luaG_runerror(L, "table index is NaN");
return newkey(L, t, key);
}
}
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
TValue *oldval = luaH_set(L, h, key); // 这里取到value
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
setobj2t(L, oldval, val); // 将值设进去
luaC_barriert(L, h, val);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTM(L, tm, t, key, val);
return;
}
t = tm; /* else repeat with `tm' */
}
luaG_runerror(L, "loop in settable");
}

rehash:

0. t->array数组,t->node是哈希表的桶。

  1. 只有正整数才允许放到array中,其他key必然都放到哈希表。
  2. 新t->array使用率必须超过50%总table元素个数,能放多少就放多少。
  3. 新t->node使用率必须超过50%,装下非array的部分。

解读:

  • 如果旧table当做数组来用,那么rehash之后所有元素都放在新t->array部分,新t->node指向dummynode。
  • 如果旧table的key都是正整数,但是比较零散,有可能新t->array极小。
  • 如果旧table的key都是正整数,但是删除了部分key,有可能新t->array比原来还小。
  • 如果旧table的key大部分都是非正整数,有可能所有key都被存哈希表了,t->array为NULL。
  • 如果旧table的key都是非正整数,则t->array必然是NULL。
  • t->array和t->node的长度都只可能是2的幂次。

哈希表部分解决冲突:

1. 通过将key1哈希到t->node上的某个位置,称为main position(简称mp)。

2. 如果mp1空闲,则直接存在mp1。若已被key2占用,计算key2的为mp2。

3. 若mp2不等于mp1,则将key1放在mp1位置上,再为key2找个其他空闲位置。

4. 若mp2等于mp1,则再为key1重新找个位置。

方案:桶+挂链。

大致的思想:为了节省内存,链表上的节点也是桶节点。即冲突的时候,在桶里面随便找一个空节点存放,再链接起来即可。

插入:如果通过哈希算出的桶节点空闲,直接使用。

若不空闲,分两种情况:

1)这个位置上的节点和新节点具有同样的哈希值。那这个节点肯定是桶头,随便找个空节点存放新节点,挂到桶头的后面。

2)这个位置上的节点和新节点具有不同的哈希值。那这个节点肯定是属于其他链表的,帮它随便找个位置放。新节点就是这个位置的桶头。

找空闲位置:有个空闲指针,从后往前移动,空节点就是可用的。若找不到空节点,持续到t->node头,就会触发rehash。

LUA的table实现的更多相关文章

  1. 打印Lua的Table对象

    小伙伴们再也不用为打印lua的Table对象而苦恼了, 本人曾也苦恼过,哈哈 不过今天刚完成了这个东西, 以前在网上搜过打印table的脚本,但是都感觉很不理想,于是,自己造轮子了~ 打印的效果,自己 ...

  2. lua的table表处理 及注意事项

    lua,一款很轻量级很nice很强大的脚本语言,做为lua中使用最为频繁的table表,在使用之时还是有颇多的好处与坑的: 下面是大牛 云风的一片关于lua table的blog,可使得对lua ta ...

  3. lua weak table 概念解析

    lua weak table 经常看到lua表中有 weak table的用法, 例如: weak_table = setmetatable({}, {__mode="v"}) 官 ...

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

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

  5. Linux下C/C++和lua交互-Table

    本来这些文章都是在我的个人网站www.zhangyi.studio,目前处在备案状态,暂时访问不了,所以搬到这边.  最近这两天需要弄清楚C++和lua间相互调用和数据传递,废话不多说,直接上过程. ...

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

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

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

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

  8. C调用lua的table里面的函数

    网上搜索C.C++调用lua函数,有一大堆复制粘贴的. 但是搜索<C调用lua的table里面的函数> 怎么就没几个呢? 经过探索,发现其实逻辑是这样的: 1.根据name获取table ...

  9. lua 的 table 处理

    lua 的整体效率是很高的,其中,它的 table 实现的很巧妙为这个效率贡献很大. lua 的 table 充当了数组和映射表的双重功能,所以在实现时就考虑了这些,让 table 在做数组使用时尽量 ...

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

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

随机推荐

  1. 牛客小白月赛11 Rinne Loves Xor

    题目链接:https://ac.nowcoder.com/acm/contest/370/I code: #include<bits/stdc++.h> using namespace s ...

  2. 【线性代数】6-1:特征值介绍(Introduction to Eigenvalues)

    title: [线性代数]6-1:特征值介绍(Introduction to Eigenvalues) categories: Mathematic Linear Algebra keywords: ...

  3. Spring Cloud Config(三):基于JDBC搭建配置中心

    1.简介 本文主要内容是基于jdbc搭建配置中心,使应用从配置中心读取配置信息并成功注册到注册中心,关于配置信息表结构仅供参考,大家可以根据具体需要进行扩展. 2.Config Server 搭建 2 ...

  4. js面向对象入门

    通常我们写js以及调用: function init(){ console.log("init") } function load(){ console.log("loa ...

  5. ubuntu hadoop安装

    参考: https://wangchangchung.github.io/2017/09/28/Ubuntu-16-04%E4%B8%8A%E5%AE%89%E8%A3%85Hadoop%E5%B9% ...

  6. python与统计(龙族版)

    因为我很喜欢龙族,额........我也很喜欢python这门语言.然后就结合了一下,用python了解了一下龙族四本书的人物出场次数及排名. <龙族1火之晨曦> 路明非 1877 诺诺 ...

  7. hadoop1.2.1安装配置

    原文地址 环境:ubuntu13 使用的用户为普通用户.如:用户ru jdk安装略 1.安装ssh (1) sudo apt-get install openssh-server (2)配置ssh面密 ...

  8. ubuntu 命令行以及目录的显示改为英文

    在安装ubuntu的时候原本以为把ubuntu改为中文的好理解一些,最后发现命令行的报错一边中文一边英文确实难受,也不宜于学习,所以想把命令行以及目录的显示都改为英文的,我是这样先把命令行的该为英文的 ...

  9. JAVA 基础编程练习题18 【程序 18 乒乓球赛】

    18 [程序 18 乒乓球赛] 题目:两个乒乓球队进行比赛,各出三人.甲队为 a,b,c 三人,乙队为 x,y,z 三人.已抽签决定比赛名单. 有人向队员打听比赛的名单.a 说他不和 x 比,c 说他 ...

  10. python基础--字符串、元组

    字符串方法 注:字符串和元组一样都是不可变的,以下函数不会改变原来字符串的值,可以将其赋给一个新的变量 st='a b c d,f,g' st_list=st.split()#字符串分割,参数不写,按 ...