自己动手实现Lua(四)lua表实现

Lua在5.0版本之前是简单使用哈希表来实现Lua表的,不过由于在实践中数组的使用非常频繁,所以为了专门优化数组的效率,Lua5.0开始改用混合数据结构来实现表。

简单说,这种混合数据结构同事包含了数组和哈希表两部分。如果表的键是连续的正整数,那么哈希表就是空的,值全部按索引存储在数组里。这样,lua数组无论是在空间利用率上(不需要显式存储键),还是在时间效率上(可以按索引存取值,不需要计算哈希码)都和真正的数组相差无几。如果表没有被当成数组使用,那么数据完全存储在哈希表里,数组部分是空的,也没什么损失。

简单实现:

 public class LuaTable
    {
        public List<LuaValue> arr;
        public Dictionary<LuaValue,LuaValue> _map;

        public static LuaTable NewLuaTable(int nArr,int nRec)
        {
            LuaTable t = new LuaTable();
            //如果参数nArr大于0,说明表可能是当作数组使用的,先创建数组部分;
            if(nArr > 0)
            {
                t.arr = new List<LuaValue>(nArr);
            }
            //如果参数nRec大于0,说明表可能是当作记录使用的,先创建哈希表部分。
            if (nRec > 0)
            {
                t._map = new Dictionary<LuaValue, LuaValue>(nRec);
            }
            return t;
        }
        /// <summary>
        /// get()方法根据键从表里查找值。如果键是整数(或者能够转换为整数的浮点数),且在数组索引范围之内,
        /// 直接按索引访问数组部分就可以了;否则从哈希表查找值。
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public LuaValue Get(LuaValue key)
        {
            int index = KeyToIntger(key);
            if(index != -1)
            {
                if(index >= 1 && index <= arr.Count)
                {
                    return arr[index - 1];
                }
            }
            return _map[key];
        }
        public int KeyToIntger(LuaValue key)
        {
            key.ConvertToInteger(out long i,out bool b);
            if(b)
            {
                return (int)i;
            }
            return -1;
        }
        public void Put(LuaValue key,LuaValue val)
        {
            if(key.IsNull())
            {
                Console.WriteLine("table index is nil!");
                return;
            }
            key.ConvertToInteger(out long i,out bool b);
            int idx = (int)i;
            if(b && idx >= 1)
            {
                if(idx <= arr.Count)
                {
                    arr[idx - 1] = val;
                    //向数组里放入nil值会制造洞,如果洞在数组末尾的话,调用_shrinkArray()函数把尾部的洞全部删除。
                    if (idx == arr.Count && val.IsNull())
                    {
                        ShrinkArray();
                    }
                    return;
                }
                //如果键是整数,而且刚刚超出数组索引范围且值不是nil,就把值追加到数组末尾,然后调用_expandArray()函数动态扩展数组。
                if (idx == arr.Count + 1)
                {
                    if(_map != null)
                    {
                        _map.Remove(key);
                    }
                    
                    if(!val.IsNull())
                    {
                        arr.Add(val);
                        ExpandArray();
                    }
                    return;
                }
            }
            //如果值不是nil就把键值对写入哈希表,否则把键从哈希表里删除以节约空间。
            if (!val.IsNull())
            {
                if(_map == null)
                {
                    _map = new Dictionary<LuaValue, LuaValue>(8);
                }
                _map[key] = val;
            }
            else
            {
                _map.Clear();
                _map = null;
            }
        }
        public long Len()
        {
            return (long)arr.Count;
        }
        /// <summary>
        /// 把尾部的洞全部删除
        /// </summary>
        public void ShrinkArray()
        {
            for(int i=0;i<arr.Count;i++)
            {
                if(arr[i].IsNull())
                {
                    arr.RemoveAt(i);
                }
            }
        }
        /// <summary>
        /// 在数组部分动态扩展之后,把原本存在哈希表里的某些值也挪到数组里
        /// </summary>
        public void ExpandArray()
        {
            if (_map == null)
            {
                return;
            }

            //将dict中从当前数组长度+1的连续整数key的value移动到数组部分
            //比如数组长度为3 就将字典里key分别为4,5,6,7....的value移动到数组

            int index = arr.Count + 1;
            while (true)
            {
                LuaValue key = new LuaValue(index);
                if (_map.TryGetValue(key, out LuaValue data))
                {
                    arr.Add(data);
                    _map.Remove(key);
                    index++;
                }
                else
                {
                    break;
                }
            }
        }
    }

 

posted @ 2023-06-01 11:34  mc宇少  阅读(75)  评论(0编辑  收藏  举报