lua表是一种常用的数据结构,底层实现分为散列表和数组。
表可以理解为引用类型,也就是说,如果把一个表的对象赋值给另一个变量,那么得到的其实是这个表的内存地址。
例子1:
local t10={1,2,3,4,5,6}
local t9=t10
t9[1]=10
print(t10[1])
这里会打印10,也就是说修改这个变量会影响到原来的表。
如果不希望影响原来的表就可以重新分配一个表,把原来的表深度复制到重新分配的表里。
首先遍历一个表,判断它的键,值是否是一个表,如果是一个表进行递归拷贝。
这里需要考虑一种情况,就是带环的情况。
什么是环:一个简单的例子:
local t={1,2,3}
t[4]={t}
这里第四个元素指向它 本身, 这个就是环的情况。
这里使用一个表去存遍历过的表,把table的内存地址当做key去存。
这样每次遍历进来都存一下,无论嵌套的有多深,如果是环的情况,它的内存地址是不会变的,
也就是说key是不会变的,因此不用担心覆盖的问题。
function table.deepcopy(tb,deep,filter)
local t={}
deep = deep or 0
deep = deep + 1
filter = filter or {}
filter[tb] =t
for k, v in pairs(tb) do
if type(v)=="table" then
if filter[v] then
t[k]= filter[v]
else
t[k]=table.deepcopy(v,deep+1,filter)
filter[v]=t[k]
end
else
t[k]=v
end
end
return t
end
这个是打印 结果: 虽然是实现了部分的深度拷贝,
但是这种实现其实还有个缺陷,就是当一个表的key是table时,就没有对key进行copy。
function table.deepcopy(object)
local filter_table={}
local function _copy(object)
if type(object)~="table" then
return object
elseif filter_table[object] then--如果已经拷贝过了,取拷贝过的
return filter_table[object]
end
local new_table={}
for k, v in pairs(object) do
new_table[_copy(k)]=_copy(v) --对键 值 进行拷贝
end
filter_table[object]=new_table --緩存起來
return setmetatable(new_table,getmetatable(object))--元表也 一起拷貝
end
return _copy(object)
end
这种 实现 也是类似,不过还把 元表设置了,还考虑多一种情况,就是一个表的key为table时。
首先会处理是否是表的情况,如果不是 直接返回,如果是 就从filter_table里去取缓存。
原理和上面的一样,都是再用一个表去存之前递归过的。
然后 如果是表,并且没有缓存,
就进行 遍历 递归拷贝,存到 new_table 新表里,但是这里有点不同的是 对key 也进行了copy,
这种情况就是
重新copy出一个后作为key再重新存到 new_table里,再对value进行递归拷贝。
有时候由不希望拷贝太深,这个时候可以添加个深度。
这种只是在上面的基础上添加了一个深度。
function table.deepcopy(tb,deep)
local t={}
local function _copy(obj,filter,_deep)
filter = filter or {}
_deep=_deep+1
if deep then
if _deep>deep then
return
end
end
if type(obj)~= "table" then
return obj
elseif filter[obj] then
return filter[obj]
end
local newT={}
for k, v in pairs(obj) do
newT[_copy(obj,filter)] =_copy(obj,filter)
end
filter[obj]= newT
return setmetatable(newT,getmetatable(obj))
end
return _copy(tb,{},deep)
end
在lua/lua版本 / src /lobject.h中 可以看到下面的结构体定义
typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<
lu_byte lsizenode; /* log2 of size of 'node' array */
unsigned int sizearray; /* size of 'array' array */
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
struct Table *metatable;
GCObject *gclist;
} Table;
CommonHeader 表示这个数据类型需要进行 垃圾 回收(GC) flags 表 元方法 有没被使用过,也就是存不存在; 1表示没有 sizearray 表示 数组 的大小
*array 就是 数组了 这里是指针
*node 是散列桶数组的起始位置指针 lastfree 散列桶数组最后位置的指针
*metatable 元表的指针
*gclist GC相关链表指针