概述
在go语言中,官方已经替我们实现了一个双向链表,可以用来存储、查找我们的数据,数据类型支持任意类型,其中节点的定义如下:
// Element is an element of a linked list.
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element
// The list to which this element belongs.
list *List
// The value stored with this element.
Value interface{}
}
具体对节点的增删改查用法如下
初始化一个双向链表
mylist := list.New()
插入节点
插入链表尾部
PushBack(v interface{}) *Element
插入链表头部
PushFront(v interface{}) *Element
指定位置插入
InsertBefore(v interface{}, mark *Element) *Element
删除节点
Remove(e *Element) interface{}
读取节点
读取头节点
Front() *Element
读取末节点
Back() *Element
读取整张链表
for e:=mylist.Front();e!=nil;e=e.Next(){
fmt.Printf("%s->",e.Value)
}
完整测试代码
package main
import (
"container/list"
"fmt"
)
func main() {
mylist := list.New()
mylist.PushFront("Begin")
mylist.PushBack("End")
mylist.InsertBefore("before end",mylist.Back())
mylist.InsertAfter("after begin",mylist.Front())
fmt.Println("Head node:",mylist.Front().Value)
fmt.Println("End node:",mylist.Back().Value)
for e:=mylist.Front();e!=nil;e=e.Next(){
fmt.Printf("%s->",e.Value)
}
//Modify the node value
mylist.Front().Value="New Begin"
fmt.Println("\nAfter update the head node")
for e:=mylist.Front();e!=nil;e=e.Next(){
fmt.Printf("%s->",e.Value)
}
//Delete node
mylist.Remove(mylist.Front())
fmt.Println("\nAfter remove the head node")
for e:=mylist.Front();e!=nil;e=e.Next(){
fmt.Printf("%s->",e.Value)
}
}
测试结果
Head node: Begin
End node: End
Begin->after begin->before end->End->
After update the head node
New Begin->after begin->before end->End->
After remove the head node
after begin->before end->End->
高并发
在高并发的goroutine中,双向链表同slice,map一样,无法保证其数据安全性,在插入节点的过程中,会丢失部分节点数据。
如以下代码
func main(){
mylist := list.New()
wg := &sync.WaitGroup{}
wg.Add(10000)
for i:=0;i<10000;i++{
go func(i int){
defer wg.Done()
mylist.PushBack(i)
}(i)
}
wg.Wait()
fmt.Println("link list length:",mylist.Len())
}
输出结果
link list length: 9795
解决方案
新建一个结构体,将list.List和sync.Mutex匿名嵌入进去,形成一个有锁属性的list结构体
结构体声明如下:
type listLock struct {
list.List
sync.Mutex
}
测试
func main(){
mylist := listLock{}
wg := &sync.WaitGroup{}
wg.Add(1000)
for i:=0;i<1000;i++{
go func(i int){
defer wg.Done()
//对链表进行加锁,此时其他goroutine需等待解锁后方可进行PushBack操作
mylist.Lock()
mylist.PushBack(i)
//对链表解锁
mylist.Unlock()
}(i)
}
wg.Wait()
fmt.Println("link list length:",mylist.Len())
}
此时的结果方为正确的结果
link list length: 1000