saveheat.go

package priorityqueue

import (
	"container/heap"
	"sort"
	"sync"
)

//something we manager in a priority queue
type Item struct {
	Key interface{} //the unique key of item
	Value interface{} //the value of the item
	Priority int //the priority of the item in the queue

	//heap.Interface need this index and update them
	Index int //index of the item in the heap
}

type ItemSlice struct {
	items []*Item
	itemsMap map[interface{}] *Item //find item according to the key (inteface {} type)
}

func (s ItemSlice) Len() int{return len(s.items)}
func (s ItemSlice) Less(i, j int) bool {
	return s.items[i].Priority < s.items[j].Priority
}

func (s ItemSlice) Swap(i, j int) {
	s.items[i], s.items[j] = s.items[j], s.items[i]
	s.items[i].Index = i
	s.items[j].Index = j
	if s.itemsMap != nil {
		s.itemsMap[s.items[i].Key] = s.items[i]
		s.itemsMap[s.items[j].Key] = s.items[j]
	}
}

func (s *ItemSlice) Push(x interface{}) {
	n := len(s.items)
	item := x.(*Item)
	item.Index = n
	s.items = append(s.items, item)
	s.itemsMap[item.Key] = item
}

func (s *ItemSlice) Pop() interface{} {
	old := s.items
	n := len(old)
	item := old[n-1]
	item.Index = -1
	delete(s.itemsMap, item.Key)
	s.items = old[0 : n-1]
	return item
}

func (s *ItemSlice) Update(key interface{}, value interface{}, priority int) {
	item := s.itemByKey(key)
	if item != nil {
		s.updateItem(item, value, priority)
	}

}

func (s *ItemSlice) itemByKey(key interface{}) *Item {
	if item, found := s.itemsMap[key]; found {
		return item
	}
	return nil
}

func (s *ItemSlice) updateItem(item *Item, 
	value interface{}, priority int ) {
	item.Value = value
	item.Priority = priority
	heap.Fix(s, item.Index)
}


type PriorityQueue struct {
	slice ItemSlice
	maxSize int 
	mutex sync.RWMutex
}

func (pq *PriorityQueue) Init(maxSize int) {
	pq.slice.items = make([]*Item, 0, pq.maxSize)
	pq.slice.itemsMap = make(map[interface{}]*Item)
	pq.maxSize = maxSize
}

func (pq PriorityQueue) Len() int {
	pq.mutex.RLock()
	size := pq.slice.Len()
	pq.mutex.RUnlock()
	return size
}

func (pq *PriorityQueue) minItem() *Item {
	len := pq.slice.Len()
	if len == 0 {
		return nil
	}
	return pq.slice.items[0]
}

func (pq *PriorityQueue) MinItem() *Item {
	pq.mutex.RLock()
	defer pq.mutex.RUnlock()
	return pq.minItem()
}

func (pq *PriorityQueue) PushItem(key, value interface{}, 
	priority int) (bPushed bool) {
	pq.mutex.Lock()
	defer pq.mutex.Unlock()
	size := pq.slice.Len()
	item := pq.slice.itemByKey(key)
	if size > 0 && item != nil {
		pq.slice.updateItem(item, value, priority)
		return true
	}
	item = &Item {
		Value: value,
		Key: key,
		Priority: priority,
		Index: -1,
	}
	if pq.maxSize <= 0 || size < pq.maxSize {
		heap.Push(&(pq.slice), item)
		return true
	}
	min := pq.minItem()
	if min.Priority >= priority {
		return false
	}
	heap.Pop(&(pq.slice))
	heap.Push(&(pq.slice), item)
	return true
}

func (pq *PriorityQueue) PopItem() interface {} {
	pq.mutex.Lock()
	defer pq.mutex.Unlock()
	sz := pq.slice.Len()
	if sz > 0 {
		return heap.Pop(&(pq.slice)).(*Item).Value
	} else {
		return nil
	}
}

func (pq PriorityQueue) GetQueue() []interface{} {
	items := pq.GetQueueItems()
	values := make([]interface{}, len(items))
	for i := 0; i < len(items); i++ {
		values[i] = items[i].Value
	}
	return values
}

func (pq PriorityQueue) GetQueueItems() []*Item {
	size := pq.Len()
	if size == 0 {
		return [] *Item{}
	}
	s := ItemSlice{}
	s.items = make([]*Item, size)
	pq.mutex.RLock()
	for i := 0; i < size; i++ {
		s.items[i] = &Item{
			Value: pq.slice.items[i].Value,
			Priority: pq.slice.items[i].Priority,
		}
	}
	pq.mutex.RUnlock()	
	sort.Sort(s)
	return s.items
}

saveheat_test.go

package priorityqueue

import (
	"fmt"
	"testing"
)

type EatFruit struct {
	key string
	UintPrice float64
	Cnt int 
	Pri int	
}

func (eat* EatFruit)Print() {
		fmt.Printf("%s, UintPrice:%v, Cnt:%v\n",eat.key, eat.UintPrice, eat.Cnt )
}

func TestPriorityQue(t *testing.T) {
	//create a priority queue, put the items in it
	items := [] EatFruit {
		 EatFruit{
		 	key: "Banana",
			UintPrice:2.4, 
			Cnt:10, Pri:4, 				
		}, 			
		EatFruit{
			key: "Apple",
			UintPrice:2.4, 
			Cnt:10, Pri:7, 
		},

		EatFruit{
			key: "Orange",
			UintPrice:2.4, 
			Cnt:10, Pri:6,
		},		
	}
	
	pq := &PriorityQueue {
		maxSize :  32,		
	} 
	pq.Init(pq.maxSize)	
	
	for _, item := range items {
		i := item
		pq.PushItem(i.key, &i, i.Pri)
	}
	fmt.Println("Before update Apple data:")
	for _, item := range pq.GetQueueItems() {
		eat := item.Value.(*EatFruit)
		eat.Print()
	}
	fmt.Println("Updated Apple data and PopItem:")
	i := &EatFruit {
		key: "Apple",
		UintPrice:69, 
		Cnt:3, Pri:2, 
	}
	pq.PushItem(i.key, i, i.Pri)	
	item := pq.PopItem()	
	if item != nil {
		item := item.(*EatFruit)
		item.Print()
	}
	fmt.Println("After PopItem:")
	for _, item := range pq.GetQueueItems() {
		eat := item.Value.(*EatFruit)
		eat.Print()
	}
	
}

测试结果:

//testing:
root@ubuntu01:priorityqueue# go test

Before update Apple data:

Banana, UintPrice:2.4, Cnt:10, Priority:4

Orange, UintPrice:5, Cnt:60, Priority:6

Apple, UintPrice:300, Cnt:4, Priority:7

Updated Apple data and PopItem:

Apple, UintPrice:69, Cnt:3, Priority:2

After PopItem:

Banana, UintPrice:2.4, Cnt:10, Priority:4

Orange, UintPrice:5, Cnt:60, Priority:6

PASS