支持分页的环形队列

支持分页的环形队列

  • 源码
  • 解析
    • [PageCircularQueue 类](#PageCircularQueue 类)
    • [readonly 函数](#readonly 函数)
    • [PageCircularQueue.new 函数](#PageCircularQueue.new 函数)
    • [PageCircularQueue:enqueue 函数](#PageCircularQueue:enqueue 函数)
    • [PageCircularQueue:dequeue 函数](#PageCircularQueue:dequeue 函数)
    • [PageCircularQueue:peek 函数](#PageCircularQueue:peek 函数)
    • [PageCircularQueue:isFull 函数](#PageCircularQueue:isFull 函数)
    • [PageCircularQueue:isEmpty 函数](#PageCircularQueue:isEmpty 函数)
    • [PageCircularQueue:getSize 函数](#PageCircularQueue:getSize 函数)
    • [PageCircularQueue:getRawPage 函数](#PageCircularQueue:getRawPage 函数)
    • [PageCircularQueue:getPage 函数](#PageCircularQueue:getPage 函数)

最近我因工作需要使用环形队列,并在常规环形队列上拓展为支持分页环形队列,用于高效地管理大量数据,支持高效的元素添加、删除及分页数据的访问。通过分页的方式,它可以有效地管理大规模的数据集合

源码

lua 复制代码
-- 创建一个分页的环形队列
local PageCircularQueue = {}
PageCircularQueue.__index = PageCircularQueue

local function readonly(t)
    local mt = {
        __index = t,
        __newindex = function (t, k, v) error("Attempt to modify read-only table", 2) end,
        __pairs = function () return pairs(t) end,
        __ipairs = function () return ipairs(t) end,
        __len = function () return #t end,
        __metatable = false
    }
    return setmetatable({}, mt)
end

--tips: head指向的当前队列中最早入队的元素,tail指向的为最新入队的元素

---创建新的环形队列对象
---@param max_pages number @最大页
---@param page_capacity number @每页最大数量
---@field head_page number @最新页
---@field tail_page number @最尾页
---@field head number @头元素所在页的位置
---@field tail number @尾元素所在页的位置
---@field max_size number @队列容量
---@field size number @队列长度
---@field pages table @页数据
function PageCircularQueue.new(max_pages, page_capacity)
    assert(max_pages > 0, "invalid max pages")
    assert(page_capacity > 0, "invalid page capacity")

    local self = setmetatable({}, PageCircularQueue)
    self.max_pages = max_pages
    self.page_capacity = page_capacity
    self.pages = {} -- 存储每个页的数据
    for i = 1, max_pages do
        self.pages[i] = {}
    end
    self.head_page = 1
    self.tail_page = 1
    self.head = 1
    self.tail = 0
    self.max_size = self.max_pages * self.page_capacity
    self.size = 0
    return self
end

-- 向队列中添加数据
function PageCircularQueue:enqueue(value)
    if value == nil then
        INFO("PageCircularQueue enqueue value is nil")
        return false
    end

    if self.size == self.max_size then
        -- 队列已满,覆盖最旧的元素
        if self.head == self.page_capacity then
            self.head = 1
            self.head_page = (self.head_page % self.max_pages) + 1
        else
            self.head = (self.head % self.page_capacity) + 1
        end
    else
        self.size = self.size + 1
    end

    self.tail = (self.tail % self.page_capacity) + 1
    self.pages[self.tail_page][self.tail] = value
    if self.tail == self.page_capacity then
        self.tail_page = (self.tail_page % self.max_pages) + 1
    end
    
    return true
end

-- 从队列中移除数据
function PageCircularQueue:dequeue()
    if self.size == 0 then
        return nil
    end

    local value = self.pages[self.head_page][self.head]
    -- self.pages[self.head_page][self.head] = nil  -- 因为会返回raw elements做遍历重建,删除仅做游标移动,不真正删除元素,保障为数组格式

    if self.head == self.page_capacity then
        self.head = 1
        self.head_page = (self.head_page % self.max_pages) + 1
    else
        self.head = (self.head % self.page_capacity) + 1
    end

    self.size = self.size - 1
    return value
end

function PageCircularQueue:peek()
    if self.size == 0 then
        return nil
    end

    return self.pages[self.head_page][self.head]
end

-- 检查队列是否已满
function PageCircularQueue:isFull()
    return self.size >= self.max_size
end

-- 检查队列是否为空
function PageCircularQueue:isEmpty()
    return self.size == 0
end

-- 获取队列的大小
function PageCircularQueue:getSize()
    return self.size
end

-- 获取指定页的详细数据
function PageCircularQueue:getRawPage(page_index)
    if page_index < 1 or page_index > self.max_pages then
        return false, "page index out of bounds " .. page_index
    end

    page_index = (self.head_page + page_index - 2) % self.max_pages + 1

    local elems = {}
    local index = self.head
    for i = 1, self.page_capacity do
        local target = self.pages[page_index][index]
        if not target then break end
        
        elems[i] = target
        if index == self.page_capacity then
            index = 1
            page_index = (page_index % self.max_pages) + 1
        else
            index = (index % self.page_capacity) + 1
        end
    end

    return true, readonly(elems)
end

function PageCircularQueue:getPage(page_index)
    if page_index < 1 or page_index > self.max_pages then
        return false, "page index out of bounds " .. page_index
    end

    
    local begin_page_index = (self.head_page + page_index - 2) % self.max_pages + 1
    local end_page_index = nil
    -- 需额外多发下一页
    if self.head ~= 1 then
        end_page_index = (begin_page_index % self.page_capacity) + 1
    end
    return true, self.head, self.page_capacity, readonly(self.pages[begin_page_index] or {}), end_page_index and readonly(self.pages[end_page_index] or {}) or {}
end

return PageCircularQueue

--[[
    使用示例
    for i = 1, 115 do
        self:enqueue(i)
    end

    local res, page_elems = self:getRawPage(1)
    for k, v in ipairs(page_elems) do
        DEBUG("res==> ", k, inspect(v)) 
    end

    只需通过getPage()将页数据同步到客户端,由客户端根据head page_capacity按需获取队列元素
    例:head为1,则只需变量self.pages[begin_page_index]中数据即可
    里:head为5,则1~6遍历self.pages[begin_page_index],7~10遍历self.pages[end_page_index]中数据
]]--

解析

PageCircularQueue 类

  • PageCircularQueue 是一个表,用于表示环形队列的数据结构
  • PageCircularQueue.__index = PageCircularQueue 设置元表,允许 PageCircularQueue 的实例通过 __index 查找方法和属性

readonly 函数

  • readonly(t) 返回一个只读表,任何对表 t 的修改操作都会引发错误。这个表可以安全地暴露给外部使用者,用于限制外部修改其内容

PageCircularQueue.new 函数

这个函数用于创建一个新的 PageCircularQueue 实例

  • max_pages:最大页数,即队列的页数
  • page_capacity:每页的最大容量
  • self.pages:一个表,用于存储每页的数据。初始化时,每页都是一个空表
  • self.head_page 和 self.tail_page:分别指示队列的头页和尾页
  • self.head 和 self.tail:指示当前页内头元素和尾元素的位置
  • self.max_size:队列的总容量
  • self.size:当前队列中的元素数量

PageCircularQueue:enqueue 函数

这个方法用于向队列中添加元素

  • 如果队列已满(self.size == self.max_size),它会覆盖最旧的元素,即 head 指向的元素
  • 如果 self.head 达到当前页的最大容量(self.page_capacity),则需要将 self.head 重置为1,并且 self.head_page 移动到下一页
  • 更新 self.tail 和 self.tail_page 以插入新元素
  • self.size 增加1

PageCircularQueue:dequeue 函数

这个方法用于从队列中移除元素

  • 如果队列为空(self.size == 0),返回 nil
  • 移除 self.head_page 页中的 self.head 位置的元素
  • 更新 self.head 和 self.head_page,使其指向下一个元素
  • self.size 减少1

PageCircularQueue:peek 函数

这个方法用于查看队列的头元素

  • 如果队列为空,返回 nil
  • 否则,返回 self.pages[self.head_page][self.head]

PageCircularQueue:isFull 函数

这个方法用于检查队列是否已满

  • 如果 self.size 大于或等于 self.max_size,返回 true,否则返回 false

PageCircularQueue:isEmpty 函数

这个方法用于检查队列是否为空

  • 如果 self.size 等于0,返回 true,否则返回 false

PageCircularQueue:getSize 函数

这个方法返回队列的当前大小,即 self.size

PageCircularQueue:getRawPage 函数

这个方法用于获取指定页的所有数据

  • page_index 需要在有效范围内(1 到 self.max_pages)
  • 计算页的实际索引 page_index,从 self.head_page 开始,读取该页的数据并返回
  • 使用 readonly 包装返回的数据,确保外部无法修改

PageCircularQueue:getPage 函数

这个方法用于获取指定页的数据及其前后的页数据

  • page_index 需要在有效范围内(1 到 self.max_pages)
  • 计算起始页的实际索引,并且如果需要,还会获取下一页的数据
  • 返回的数据被包装为 readonly 表,确保数据的安全性
相关推荐
0白露43 分钟前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.2 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐2 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
想跑步的小弱鸡2 小时前
Leetcode hot 100(day 3)
算法·leetcode·职场和发展
战族狼魂2 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
Tttian6223 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
爱的叹息4 小时前
RedisTemplate 的 6 个可配置序列化器属性对比
算法·哈希算法
hycccccch4 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq