[LuaHashMap] 严格的 Lua HashMap 实现

说点什么

上次介绍了一个较为严格的 Lua 数组的实现——LuaArray,这次奉上 LuaHashMap 的实现。当然,机制和LuaArray 一样,我们就不介绍了。

基本要求

  1. 除了nil,任何值都可以作为键;
  2. 可通过键直接访问和修改值;
  3. 对不存在的键进行赋值将创建新的键值对映射;
  4. 可开启键值数据类型过滤,即类似严格类型映射: HashMap(String, String),非匹配的键值映射将被过滤或做无效处理;
  5. 提供基本的方法来操作键值对。

硬性约束

  1. 不允许直接修改内置的方法,如有需要,请手动扩展 __methods__ 列表;

实现

** 1. 构造 **
LuaArray ,首先我们需要创建一个 HashMap 的改造方法Map(),其中约定 new_map 为构造返回的 HashMap 实例, __map__用于存储键值对映射,__methods__ 用于存储内置的键值对映射的操作方法:

function Map()
    local new_map = {}
    local __map__ = {}
    local __methods__ = {}
    -- do something to construct a map
    return new_map
end

** 2. 元表 **
元表需要提供了访问映射表和内置方法的途径,以及新增或修改一对映射的流程:

function Map()
    local new_map = {}
    local __map__ = {}
    local __methods__ = {}

    local mt = {
        __index = function(t, k)
            -- 可以获得根据键获得值
            if __map__[k] then
                return __map__[k]
            end
            -- 可以获得内置方法
            if __methods__[k] then
                return __methods__[k]
            end
        end,
        __newindex = function(t, k, v)
            -- 首先检查内置方法列表中是否存在对应键,
            -- 为了避免思维的理解误区,我们不建议在
            -- __map__中存储与__methods__的同名映射
            if __methods__[k] then
                print('[warning] can not override native method.')
                return
            end
            -- set 方法用于设置键值对映射,我们后面再说明
            __methods__:set(k, v)
        end
    }
    setmetatable(new_map, mt)

    return new_map
end

** 3. 类型过滤**
键值对都有类型,为了保持HashMap 中的键值对类型一致,需要对映射表开启类型检查和过滤。在构造的时候,可以传入键值的类型ktypevtype,默认ktypevtype都为Mixed(混合,也就是原生态的 lua table了),接下来只要对类型进行过滤即可:

-- 表长度
function table.len(tbl)
    local count = 0
    for k, v in pairs(tbl) do
        count = count + 1
    end
    return count
end

-- 打印表
function table.print(tbl)
    local format = string.format
    for k,v in pairs(tbl) do
        print(format('[%s] => ', k), v)
    end
end

-- 以键作为值来构造表
function table.keyAsValue(...)
    local arr = {...}
    local ret = {}
    for _,v in ipairs(arr) do
        ret[v] = v
    end
    return ret
end

-- 数据类型定义
DATA_TYPE = table.keyAsValue('boolean', 'number', 'string', 'function', 'table', 'thread', 'nil')

-- 检查HashMap 的键值类型
local function checkHashType(tp)
    if not (tp == 'Mixed' or DATA_TYPE[tp]) then
        tp = 'Mixed'
    end
    return tp
end

-- HashMap 构造
function Map(ktype, vtype)
    local new_map = {}
    local __map__ = {}
    local __methods__ = {}
    local __key_type__, __value_type__ = checkHashType(ktype), checkHashType(vtype)

    -- 映射表类型
    function __methods__:typeOf()
        return string.format('HashMap<%s, %s>',__key_type__,__value_type__)
    end
    -- 映射表长度
    function __methods__:len()
        return table.len(__map__)
    end
    -- 设置一个映射
    function __methods__:set(k, v)
        -- 需要检查映射表类型
        if (__key_type__ == 'Mixed' or type(k) == __key_type__)
        and (__value_type__ == 'Mixed' or type(v) == __value_type__) then
            __map__[k] = v
        end
    end
    -- 解除一个映射
    function __methods__:unset(k)
        __map__[k] = nil
    end
    -- 打印映射表
    function __methods__:print()
        table.print(__map__)
    end
    -- 过滤键类型
    function __methods__:filterKey(tp)
        print('filter key type:',tp)
        for k,v in pairs(__map__) do
            if not checkType(type(k), tp) then
                __map__[k] = nil
            end
        end
    end
    -- 过滤值类型
    function __methods__:filterValue(tp)
        print('filter value type:',tp)
        for k,v in pairs(__map__) do
            if not checkType(type(v), tp) then
                __map__[k] = nil
            end
        end
    end
    -- 设置键类型
    function __methods__:setKeyType(type)
        if not checkType(type, nil) then
            if __key_type__ == type then
                return
            end
            __key_type__ = type
            self:filterKey(type)
        end
    end
    -- 设置值类型
    function __methods__:setValueType(type)
        if not checkType(type, nil) then
            if __value_type__ == type then
                return
            end
            __value_type__ = type
            self:filterValue(type)
        end
    end
    -- 过滤值
    function __methods__:filter(val)
        for k,v in pairs(__map__) do
            if v == val then
                __map[k] = nil
            end
        end
    end

    -- 元表
    local mt = {
        __index = function(t, k)
            if __map__[k] then
                return __map__[k]
            end
            if __methods__[k] then
                return __methods__[k]
            end
        end,
        __newindex = function(t, k, v)
            -- 首先检查内置方法列表中是否存在对应键,
            -- 为了避免思维的理解误区,我们不建议在
            -- __map__中存储与__methods__的同名映射
            if __methods__[k] then
                print('[warning] can not override native method.')
                return
            end
            -- 设置映射时需要检查映射类型
            __methods__:set(k, v)
        end
    }
    setmetatable(new_map, mt)

    return new_map
end

以上。

PS:

  1. 在线测试
  2. 完整代码
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容