网上找到一个开源的代码
https://github.com/AceDarkknight/ConcurrentSkipList
数据结构
我们用 ConcurrentSkipList 这个数据结构代表整个 skip list,可以看到里面是一个包含多个 skipList 的切片。
// ConcurrentSkipList is a struct contains a slice of concurrent skip list.
type ConcurrentSkipList struct {
skipLists []*skipList
level int
}
skipList 的结构如下, 每个 skipList 除了有头结点、尾节点、高度、长度外都有一把读写锁,负责保证并发安全。
type skipList struct {
level int
length int32
head *Node
tail *Node
mutex sync.RWMutex
}
其中我们把每个节点称为一个 Node,Node 的结构如下,index 代表节点的索引值,value 代表节点的值,nextNodes 记录了该节点指向的下个节点。
type Node struct {
index uint64
value interface{}
nextNodes []*Node
}
concurrentSkipList.go源文件
/*
Package ConcurrentSkipList provide an implementation of skip list. It's thread-safe in concurrency and high performance.
*/
package ConcurrentSkipList
import (
"errors"
"math"
"sync/atomic"
"github.com/OneOfOne/xxhash"
)
// Comes from redis's implementation.
// Also you can see more detail in William Pugh's paper <Skip Lists: A Probabilistic Alternative to Balanced Trees>.
// The paper is in ftp://ftp.cs.umd.edu/pub/skipLists/skiplists.pdf
const (
MAX_LEVEL = 32
PROBABILITY = 0.25
SHARDS = 32
)
// shardIndex is used to indicate which shard a given index belong to.
var shardIndexes = make([]uint64, SHARDS)
// init will initialize the shardIndexes.
func init() {
var step uint64 = 1 << 59 // 2^64/SHARDS
var t uint64 = math.MaxUint64
for i := SHARDS - 1; i >= 0; i-- {
shardIndexes[i] = t
t -= step
}
}
// ConcurrentSkipList is a struct contains a slice of concurrent skip list.
type ConcurrentSkipList struct {
skipLists []*skipList
level int
}
// NewConcurrentSkipList will create a new concurrent skip list with given level.
// Level must between 1 to 32. If not, will return an error.
// To determine the level, you can see the paper ftp://ftp.cs.umd.edu/pub/skipLists/skiplists.pdf.
// A simple way to determine the level is L(N) = log(1/PROBABILITY)(N).
// N is the count of the skip list which you can estimate. PROBABILITY is 0.25 in this case.
// For example, if you expect the skip list contains 10000000 elements, then N = 10000000, L(N) ≈ 12.
// After initialization, the head field's level equal to level parameter and point to tail field.
func NewConcurren