Can anyone suggest Go container for simple and fast FIF/queue, Go has 3 different containers: heap
, list
and vector
. Which one is more
Surprised to see no one has suggested buffered channels yet, for size bound FIFO Queue anyways.
//Or however many you might need + buffer.
c := make(chan int, 300)
//Push
c <- value
//Pop
x <- c
Slice can be used to implement queue.
type queue struct {
values []*int
}
func New() *queue {
queue := &queue{}
return queue
}
func (q *queue) enqueue(val *int) {
q.values = append(q.values, val)
}
//deque function
Update:
here is complete implementation on my GitHub page https://github.com/raiskumar/algo-ds/blob/master/tree/queue.go
Using a slice and an appropriate ("circular") indexing scheme on top still seems to be the way to go. Here's my take on it: https://github.com/phf/go-queue The benchmarks there also confirm that channels are faster, but at the price of more limited functionality.
Unfortunately queues aren't currently part of the go standard library, so you need to write your own / import someone else's solution. It's a shame as containers written outside of the standard library aren't able to use generics.
A simple example of a fixed capacity queue would be:
type MyQueueElement struct {
blah int // whatever you want
}
const MAX_QUEUE_SIZE = 16
type Queue struct {
content [MAX_QUEUE_SIZE]MyQueueElement
readHead int
writeHead int
len int
}
func (q *Queue) Push(e MyQueueElement) bool {
if q.len >= MAX_QUEUE_SIZE {
return false
}
q.content[q.writeHead] = e
q.writeHead = (q.writeHead + 1) % MAX_QUEUE_SIZE
q.len++
return true
}
func (q *Queue) Pop() (MyQueueElement, bool) {
if q.len <= 0 {
return MyQueueElement{}, false
}
result := q.content[q.readHead]
q.content[q.readHead] = MyQueueElement{}
q.readHead = (q.readHead + 1) % MAX_QUEUE_SIZE
q.len--
return result, true
}
Gotchas avoided here include not having unbounded slice growth (caused by using the slice [1:] operation to discard), and zeroing out popped elements to ensure their contents are available for garbage collection. Note, in the case of a MyQueueElement
struct containing only an int like here, it will make no difference, but if struct contained pointers it would.
The solution could be extended to reallocate and copy should an auto growing queue be desired.
This solution is not thread safe, but a lock could be added to Push/Pop if that is desired.
Playground https://play.golang.org/
list is enough for queue and stack, what we shoud do is l.Remove(l.Front())
for queue Poll, l.Remove(l.Back())
for stack Poll,PushBack
for the Add Operation for stack and queue. there are front and back pointer for list, such that time complexity is O(1)
Most queue implementations are in one of three flavors: slice-based, linked list-based, and circular-buffer (ring-buffer) based.
The ring-buffer based queue reuses memory by wrapping its storage around: As the queue grows beyond one end of the underlying slice, it adds additional nodes to the other end of the slice. See deque diagram
Also, a bit of code to illustrate:
// PushBack appends an element to the back of the queue. Implements FIFO when
// elements are removed with PopFront(), and LIFO when elements are removed
// with PopBack().
func (q *Deque) PushBack(elem interface{}) {
q.growIfFull()
q.buf[q.tail] = elem
// Calculate new tail position.
q.tail = q.next(q.tail)
q.count++
}
// next returns the next buffer position wrapping around buffer.
func (q *Deque) next(i int) int {
return (i + 1) & (len(q.buf) - 1) // bitwise modulus
}
This particular implementation always uses a buffer size that is a power of 2, and can therefore compute the bitwise modulus to be a little more efficient.
This means the slice needs to grow only when all its capacity is used up. With a resizing strategy that avoids growing and shrinking storage on the same boundary, this makes it very memory efficient.
Here is code that resizes the underlying slice buffer:
// resize resizes the deque to fit exactly twice its current contents. This is
// used to grow the queue when it is full, and also to shrink it when it is
// only a quarter full.
func (q *Deque) resize() {
newBuf := make([]interface{}, q.count<<1)
if q.tail > q.head {
copy(newBuf, q.buf[q.head:q.tail])
} else {
n := copy(newBuf, q.buf[q.head:])
copy(newBuf[n:], q.buf[:q.tail])
}
q.head = 0
q.tail = q.count
q.buf = newBuf
}
Another thing to consider is if you want concurrency safety built into the implementation. You may want to avoid this so that you can do whatever works best for your concurrency strategy, and you certainly do not want it if your do not need it; same reason as not wanting a slice that has some built-in serialization.
There are a number of ring-buffer based queue implementations for Go if you do a search on godoc for deque. Choose one that best suits your tastes.