The container/heap package in Go provides a convenient means to implement a priority queue, which is a data structure where each element has a priority. Elements are dequeued in order of their priority, rather than in simple insertion order. In Go, you can create a priority queue by defining a heap interface and using the functions provided in this package to maintain the heap properties.

Understanding the Heap Interface

To start with, you need to define a type that implements the heap.Interface, which is comprised of three functions: Len(), Less(i, j int) bool, Swap(i, j int), in addition to the methods Push(x interface{}) and Pop() interface{}.

package main

import (
    "container/heap"
    "fmt"
)

// An Item is something we manage in a priority queue.
type Item struct {
    value    string // The value of the item; arbitrary.
    priority int    // The priority of the item in the queue.
    index    int    // The index of the item in the heap.
}

// A PriorityQueue implements heap.Interface and holds Items.
type PriorityQueue []*Item

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
    // We want Pop to give us the highest, not lowest, priority so we use greater than here.
    return pq[i].priority > pq[j].priority
}

func (pq PriorityQueue) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
    pq[i].index = i
    pq[j].index = j
}

func (pq *PriorityQueue) Push(x interface{}) {
    n := len(*pq)
    item := x.(*Item)
    item.index = n
    *pq = append(*pq, item)
}

func (pq *PriorityQueue) Pop() interface{} {
    old := *pq
    n := len(old)
    item := old[n-1]
    old[n-1] = nil  // avoid memory leak
    item.index = -1 // for safety
    *pq = old[0 : n-1]
    return item
}

Using the Priority Queue

Once you've implemented the interface, you can start using your priority queue. You'll want to use the heap functions heap.Init, heap.Push, and heap.Pop to manipulate your priority queue.

func main() {
    // Some items and their priorities.
    items := map[string]int{
        "banana": 3, "apple": 4, "pear": 2,
    }

    // Create a priority queue, put the items in it, and
    // establish the priority queue (heap) invariants.
    pq := make(PriorityQueue, len(items))
    i := 0
    for value, priority := range items {
        pq[i] = &Item{
            value:    value,
            priority: priority,
            index:    i,
        }
        i++
    }
    heap.Init(&pq)

    // Insert a new item and then modify its priority.
    item := &Item{
        value:    "orange",
        priority: 1,
    }
    heap.Push(&pq, item)

    pq.update(item, item.value, 5)

    // Take the items out; they arrive in decreasing priority order.
    for pq.Len() > 0 {
        item := heap.Pop(&pq).(*Item)
        fmt.Printf("%s with priority %d\n", item.value, item.priority)
    }
}

// update modifies the priority and value of an Item in the queue.
func (pq *PriorityQueue) update(item *Item, value string, priority int) {
    item.value = value
    item.priority = priority
    heap.Fix(pq, item.index)
}

Conclusion

Using the container/heap package, you can efficiently maintain a priority queue tailored to your application's needs. This package significantly simplifies the creation and management of heaps in Go, offering a robust foundation for priority queue operations.