← Back to the index page

Queue

Queue is one of the simple data structures, usually used in more complex algorithms as temporary data storage. The queue is the linear FIFO data structure, all operations with the queue have O(1) complexity. Entries in the queue in a sequence can be modified by the enqueueing of entities at one end of the sequence and the removal of entities from the other end of the sequence.

There are 2 mandatory queue operations:

Also might include additional, but not mandatory:

Figure 1. Queue data structure

In Lua queue can be implemented just with a table out-of-box.

Warning

Adding nil to the queue means the end of the table and Lua iterators might work not as you expected. My general rule is never to add nil values inside any table or structure.

-- Create queue as table 
local queue = {}

-- enqueue
queue[#queue + 1] = 'A'
queue[#queue + 1] = 'B'
queue[#queue + 1] = 'C'

-- front
print(queue[1]) --> "A"

-- rear
print(queue[#queue]) --> "C"

-- size
print(#queue) --> 3

-- dequeue
print(table.remove(queue, 1)) --> "A"

-- is empty
if queue[1] == nil then
    print("Queue is empty")
else
    print("Queue is not empty")
end

In the above example, there is a large drawback. On the enqueue indexes for the next elements after the first should be recalculated. This is how table.remove method works. In this case, the complexity will be O(n-1). Let’s assume there are one million records (106), every index will be recalculated 106-1 times. This is very inefficient.

Let’s implement a wrapper class for the queue with complexity O(1).

Queue class

Class implementation with OOP principles and annotations.

Queue.lua

---@class Queue
---@field private _first number
---@field private _last number
---@field private _size number
local Queue = {}
Queue.__index = Queue

---@return Queue
function Queue:new()
    local t = {
        _first = 0,
        _last = -1,
        _size = 0,
    }
    return setmetatable(t, self)
end

---@param v any
function Queue:enqueue(v)
    local last = self._last + 1
    self._last = last
    self[last] = v
    self._size = self._size + 1
end

---@return any
function Queue:dequeue()
    if self:isEmpty() then
        return nil
    end
    local first = self._first
    local v = self[first]
    self[first] = nil -- garbage collection removes it
    self._first = self._first + 1
    self._size = self._size - 1
    return v
end

---@return boolean
function Queue:isEmpty()
    return self._first > self._last
end

---@return any
function Queue:front()
    return self[self._first]
end

---@return any
function Queue:rear()
    return self[self._last]
end

---@return number
function Queue:size()
    return self._size
end

---@params sep? string
---@return string
function Queue:toString(sep)
    sep = sep or ","
    local t = {}
    for i = self._first, self._last do
        t[#t + 1] = tostring(self[i])
    end
    return table.concat(t, sep)
end

return Queue

Usage of Queue class

local q = Queue:new()
print(q:isEmpty()) --> true
q:enqueue(11)
q:enqueue(22)
q:enqueue(33)
print(q:isEmpty()) --> false
print(q:toString()) --> "11,22,33"
print(q:size()) --> 3
print(q:front(), q:rear()) --> 11,  33
print(q:dequeue()) --> 11
print(q:dequeue()) --> 22
print(q:dequeue()) --> 33
print(q:dequeue()) --> nil
print(q:isEmpty()) --> false

References

Feedback

For feedback, please check the contacts section. Before writing, please specify where you came from and who you are. Sometimes spammers go insane. Thank you in advance for your understanding.

← Back to the index page