queue



The queue
package provides thread-safe generic implementations in Go for the following data structures: BlockingQueue
, PriorityQueue
, CircularQueue
and Linked Queue
.
A queue is a sequence of entities that is open at both ends where the elements are
added (enqueued) to the tail (back) of the queue and removed (dequeued) from the head (front) of the queue.
The implementations are designed to be easy-to-use and provide a consistent API, satisfying the Queue
interface provided by this package. .
Benchmarks and Example tests can be found in this package.
Installation
To add this package as a dependency to your project, run:
go get -u github.com/adrianbrad/queue
Import
To use this package in your project, you can import it as follows:
import "github.com/adrianbrad/queue"
Usage
Queue Interface
type Queue[T comparable] interface {
Get() (T, error)
Offer(T) error
Reset()
Contains(T) bool
Peek() (T, error)
Size() int
IsEmpty() bool
Iterator() <-chan T
Clear() []T
}
Blocking Queue
Blocking queue is a FIFO ordered data structure. Both blocking and non-blocking methods are implemented.
Blocking methods wait for the queue to have available items when dequeuing, and wait for a slot to become available in case the queue is full when enqueuing.
The non-blocking methods return an error if an element cannot be added or removed.
Implemented using sync.Cond from the standard library.
package main
import (
"fmt"
"github.com/adrianbrad/queue"
)
func main() {
elems := []int{2, 3}
blockingQueue := queue.NewBlocking(elems, queue.WithCapacity(3))
containsTwo := blockingQueue.Contains(2)
fmt.Println(containsTwo)
size := blockingQueue.Size()
fmt.Println(size)
empty := blockingQueue.IsEmpty()
fmt.Println(empty)
if err := blockingQueue.Offer(1); err != nil {
}
elem, err := blockingQueue.Get()
if err != nil {
}
fmt.Println("elem: ", elem)
}
Priority Queue
Priority Queue is a data structure where the order of the elements is given by a comparator function provided at construction.
Implemented using container/heap standard library package.
package main
import (
"fmt"
"github.com/adrianbrad/queue"
)
func main() {
elems := []int{2, 3, 4}
priorityQueue := queue.NewPriority(
elems,
func(elem, otherElem int) bool { return elem < otherElem },
)
containsTwo := priorityQueue.Contains(2)
fmt.Println(containsTwo)
size := priorityQueue.Size()
fmt.Println(size)
empty := priorityQueue.IsEmpty()
fmt.Println(empty)
if err := priorityQueue.Offer(1); err != nil {
}
elem, err := priorityQueue.Get()
if err != nil {
}
fmt.Printf("elem: %d\n", elem)
}
Circular Queue
Circular Queue is a fixed size FIFO ordered data structure. When the queue is full, adding a new element to the queue overwrites the oldest element.
Example:
We have the following queue with a capacity of 3 elements: [1, 2, 3].
If the tail of the queue is set to 0, as if we just added the element 3
,
the next element to be added to the queue will overwrite the element at index 0.
So, if we add the element 4
, the queue will look like this: [4, 2, 3].
If the head of the queue is set to 0, as if we never removed an element yet,
then the next element to be removed from the queue will be the element at index 0, which is 4
.
package main
import (
"fmt"
"github.com/adrianbrad/queue"
)
func main() {
elems := []int{2, 3, 4}
circularQueue := queue.NewCircular(elems, 3)
containsTwo := circularQueue.Contains(2)
fmt.Println(containsTwo)
size := circularQueue.Size()
fmt.Println(size)
empty := circularQueue.IsEmpty()
fmt.Println(empty)
if err := circularQueue.Offer(1); err != nil {
}
elem, err := circularQueue.Get()
if err != nil {
}
fmt.Printf("elem: %d\n", elem)
}
Linked Queue
A linked queue, implemented as a singly linked list, offering O(1)
time complexity for enqueue and dequeue operations. The queue maintains pointers
to both the head (front) and tail (end) of the list for efficient operations
without the need for traversal.
package main
import (
"fmt"
"github.com/adrianbrad/queue"
)
func main() {
elems := []int{2, 3, 4}
circularQueue := queue.NewLinked(elems)
containsTwo := circularQueue.Contains(2)
fmt.Println(containsTwo)
size := circularQueue.Size()
fmt.Println(size)
empty := circularQueue.IsEmpty()
fmt.Println(empty)
if err := circularQueue.Offer(1); err != nil {
}
elem, err := circularQueue.Get()
if err != nil {
}
fmt.Printf("elem: %d\n", elem)
}
Benchmarks
Results as of October 2023.
BenchmarkBlockingQueue/Peek-8 84873882 13.98 ns/op 0 B/op 0 allocs/op
BenchmarkBlockingQueue/Get_Offer-8 27135865 47.00 ns/op 44 B/op 0 allocs/op
BenchmarkBlockingQueue/Offer-8 53750395 25.40 ns/op 43 B/op 0 allocs/op
BenchmarkCircularQueue/Peek-8 86001980 13.76 ns/op 0 B/op 0 allocs/op
BenchmarkCircularQueue/Get_Offer-8 32379159 36.83 ns/op 0 B/op 0 allocs/op
BenchmarkCircularQueue/Offer-8 63956366 18.77 ns/op 0 B/op 0 allocs/op
BenchmarkLinkedQueue/Peek-8 1000000000 0.4179 ns/op 0 B/op 0 allocs/op
BenchmarkLinkedQueue/Get_Offer-8 61257436 18.48 ns/op 16 B/op 1 allocs/op
BenchmarkLinkedQueue/Offer-8 38975062 30.74 ns/op 16 B/op 1 allocs/op
BenchmarkPriorityQueue/Peek-8 86633734 14.02 ns/op 0 B/op 0 allocs/op
BenchmarkPriorityQueue/Get_Offer-8 29347177 39.88 ns/op 0 B/op 0 allocs/op
BenchmarkPriorityQueue/Offer-8 40117958 31.37 ns/op 54 B/op 0 allocs/op