介绍
栈是存放值的一种特殊容器,在插入与删除值时,这种结构遵循后进先出(Last-in-first-out,LIFO)的原则,也就是说,值可以任意插入栈中,但每次取出的都是此前插入的最后一个值。
实现
栈必须支持以下方法:
此外,还可以定义如下的方法:
此外,还应该提供一个类似构造器的NewStack()方法,当我们开始使用它时,它会初始化一个栈。
基于数组的简单实现
为了实现栈接口,我们可以用一个数组来存放其中的元素。
type T int
type Stack struct {
sync.RWMutex
array []T
}
构造器NewStack()方法如下:
func NewStack() *Stack {
stack := &Stack{}
stack.array = []T{}
return stack
}
接下来,我们去实现之前提到的操作方法:
// Push adds t to the top of the stack
func (s *Stack) Push(t T) {
s.Lock()
s.array = append(s.array, t)
s.Unlock()
}
对于Push()方法,只需要将值添加到数组中,Go的原生语法为我们解决了这一步骤。
// Pop removes the top element from the stack
func (s *Stack) Pop() (*T, error) {
if s.IsEmpty() {
return nil, fmt.Errorf("stack must not be empty")
}
s.Lock()
item := s.array[len(s.array)-1]
s.array = s.array[0 : len(s.array)-1]
s.Unlock()
return &item, nil
}
Pop()方法中,首先检查栈是否为空,如果栈空,则返回空值以及错误信息,否则,将数组第一位取出,整个数组右移一位。
// Size returns the size of the stack
func (s *Stack) Size() int {
s.RLock()
defer s.RUnlock()
return len(s.array)
}
func (s *Stack) IsEmpty() bool {
s.RLock()
defer s.RUnlock()
return len(s.array) == 0
}
至于额外的两个方法,即检查栈结构体中成员变量即可。注意到,栈结构体在定义时加入了锁资源,因此以上所有方法都是并发安全的。
单元测试
我们对实现的方法进行单元测试:
package stack
import "testing"
var (
t1 T = 1
t2 T = 2
t3 T = 3
)
func TestStack_Push(t *testing.T) {
stack := NewStack()
stack.Push(t1)
stack.Push(t2)
stack.Push(t3)
first := stack.array[0]
last := stack.array[len(stack.array)-1]
if first != t1 || last != t3 {
t.Errorf("wrong order, expected first 1 and last 3 but got %d and %d", t1, t3)
}
}
func TestStack_Pop(t *testing.T) {
stack := NewStack()
stack.Push(t1)
stack.Push(t2)
stack.Push(t3)
_, _ = stack.Pop()
if size := stack.Size(); size != 2 {
t.Errorf("wrong count, expected 2 and got %d", size)
}
_, _ = stack.Pop()
_, _ = stack.Pop()
if size := stack.Size(); size != 0 {
t.Errorf("wrong count, expected 0 and got %d", size)
}
_, err := stack.Pop()
if err == nil {
t.Errorf("stack must not be empty")
}
}
func TestStack_Size(t *testing.T) {
stack := NewStack()
stack.Push(t1)
stack.Push(t2)
stack.Push(t3)
if size := stack.Size(); size != 3 {
t.Errorf("wrong count, expected 3 and got %d", size)
}
}
func TestStack_IsEmpty(t *testing.T) {
stack := NewStack()
empty := stack.IsEmpty()
if !empty {
t.Errorf("wrong status, expected true and got %t", empty)
}
stack.Push(t1)
empty = stack.IsEmpty()
if empty {
t.Errorf("wrong status, expected false and got %t", empty)
}
}
至此,单元测试通过,我们就完成了栈数据结构的实现。