levelDB中的skiplist跳表 下
前言
在跳表 上 中介绍了跳表的基本结构,接下来将要介绍跳表的迭代器和其内部实现的方法。
Iterator -跳表的迭代器
跳表的迭代器实现非常简单,调用了跳表内部的一些实现方法。
// Iteration over the contents of a skip list
class Iterator {
public:
// Initialize an iterator over the specified list.
// The returned iterator is not valid.
explicit Iterator(const SkipList* list);
// Returns true iff the iterator is positioned at a valid node.
bool Valid() const;
// Returns the key at the current position.
// REQUIRES: Valid()
const Key& key() const;
// Advances to the next position.
// REQUIRES: Valid()
void Next();
// Advances to the previous position.
// REQUIRES: Valid()
void Prev();
// Advance to the first entry with a key >= target
void Seek(const Key& target);
// Position at the first entry in list.
// Final state of iterator is Valid() iff list is not empty.
void SeekToFirst();
// Position at the last entry in list.
// Final state of iterator is Valid() iff list is not empty.
void SeekToLast();
//私有成员
private:
const SkipList* list_;
Node* node_;
// Intentionally copyable
};
//...
//迭代器的构造函数
template<typename Key, class Comparator>
inline SkipList<Key,Comparator>::Iterator::Iterator(const SkipList* list) {
list_ = list;
node_ = nullptr;
}
//判断是否合法
template<typename Key, class Comparator>
inline bool SkipList<Key,Comparator>::Iterator::Valid() const {
return node_ != nullptr;
}
//返回key
template<typename Key, class Comparator>
inline const Key& SkipList<Key,Comparator>::Iterator::key() const {
assert(Valid());
return node_->key;
}
//默认从最底层链路跳到next的位置
//之前说过,第0层包含了所有的key
template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::Next() {
assert(Valid());
node_ = node_->Next(0);
}
//跳到前一个Node的位置
template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::Prev() {
// Instead of using explicit "prev" links, we just search for the
// last node that falls before key.
assert(Valid());
//FindLessThan是skipList的一个方法,后面会详细介绍
node_ = list_->FindLessThan(node_->key);
if (node_ == list_->head_) {
node_ = nullptr;
}
}
//Seek是追踪的意思
template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) {
//设置当前节点为与target的key相等或比target大的node
//FindGreaterOrEqual也是skiplist的一个函数
node_ = list_->FindGreaterOrEqual(target, nullptr);
}
//追踪到头部
template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::SeekToFirst() {
//在skiplist中有一个head_成员
node_ = list_->head_->Next(0);
}
//追踪到尾部
template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::SeekToLast() {
node_ = list_->FindLast();
if (node_ == list_->head_) {
node_ = nullptr;
}
}
skiplist内部方法
NewNode
template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node*
SkipList<Key,Comparator>::NewNode(const Key& key, int height) {
//以对齐的方式申请内存
//arena是leveldb的内存池,之后会讲到
//原子指针的大小是根据高度来定的
char* mem = arena_->AllocateAligned(
sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1));
//新建一个节点并且返回
return new (mem) Node(key);
}
RandomHeight
当要插入一个Node的时候,它应该在插入在哪些高度上呢?
在levelDB的实现中,就是使用了生成随机高度的方式。
template<typename Key, class Comparator>
int SkipList<Key,Comparator>::RandomHeight() {
// Increase height with probability 1 in kBranching
static const unsigned int kBranching = 4;
int height = 1;
//生成随机高度的一个方式
//rnd_是跳表的一个内部成员
while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) {
height++;
}
assert(height > 0);
assert(height <= kMaxHeight);
return height;
}
KeyIsAfterNode
字面意思是判断当前Key值是否在Node之后,但是这样说似乎有些突兀,其本质就是比较Key的值和Node中key值的大小(因为跳表是有序的)。
template<typename Key, class Comparator>
bool SkipList<Key,Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
// null n is considered infinite
//使用比较器
return (n != nullptr) && (compare_(n->key, key) < 0);
}
一些关于find的方法
如果对跳表的结构不够熟悉,这部分理解起来可能有些困难,建议画一个跳表,然后结合图片来进行分析(这里提供一个图片)。
//找一个key值比传入参数key大或者相等的Node并返回
template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev)
const {
Node* x = head_;
int level = GetMaxHeight() - 1;
while (true) {
//先找到当前节点的next节点
//比较key值和next节点的key值,如果key值大于next节点的key,就说明我们要找的节点在next之后
//反之,就说明我们找到了该高度中我们要找的节点
Node* next = x->Next(level);
if (KeyIsAfterNode(key, next)) {
// Keep searching in this list
//继续搜索当前高度
x = next;
} else {
//prev用于存放每一层中找到的节点的前一个节点
if (prev != nullptr) prev[level] = x;
//如果是最底层,则直接返回
if (level == 0) {
return next;
} else {
// Switch to next list
level--;
}
}
}
}
//找一个比key值比传入参数更小的节点
//如果没有就返回head
template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node*
SkipList<Key,Comparator>::FindLessThan(const Key& key) const {
Node* x = head_;
int level = GetMaxHeight() - 1;
while (true) {
assert(x == head_ || compare_(x->key, key) < 0);
Node* next = x->Next(level);
//如果移动到了当前层次尾部或者next->key比传入key值大
if (next == nullptr || compare_(next->key, key) >= 0) {
//如果到了最底层则直接返回
if (level == 0) {
return x;
} else {
// Switch to next list
//搜索下一个层次
level--;
}
} else {
//继续搜索当前层次
x = next;
}
}
}
//找到跳表的尾部
template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast()
const {
Node* x = head_;
int level = GetMaxHeight() - 1;
while (true) {
Node* next = x->Next(level);
//找到了当前层次的尾部
if (next == nullptr) {
//如果是最底层,则直接返回
if (level == 0) {
return x;
} else {
// Switch to next list
level--;
}
} else {
x = next;
}
}
}
insert
插入操作是实现中一个非常重要的部分。
void SkipList<Key,Comparator>::Insert(const Key& key) {
// TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()
// here since Insert() is externally synchronized.
//指向前一个节点的数组
Node* prev[kMaxHeight];
//调用该函数,并传入参数pre
//可以找到插入key值的节点在每一个高度中插入点的前一个位置
Node* x = FindGreaterOrEqual(key, prev);
// Our data structure does not allow duplicate insertion
assert(x == nullptr || !Equal(key, x->key));
//生成一个随机高度
int height = RandomHeight();
//如果生成的高度大于当前跳表的高度
if (height > GetMaxHeight()) {
//初始化高出的部分
for (int i = GetMaxHeight(); i < height; i++) {
prev[i] = head_;
}
//fprintf(stderr, "Change height from %d to %d\n", max_height_, height);
// It is ok to mutate max_height_ without any synchronization
// with concurrent readers. A concurrent reader that observes
// the new value of max_height_ will see either the old value of
// new level pointers from head_ (nullptr), or a new value set in
// the loop below. In the former case the reader will
// immediately drop to the next level since nullptr sorts after all
// keys. In the latter case the reader will use the new node.
max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
}
//新建一个节点
x = NewNode(key, height);
//将该节点插入到之前找到的位置上去
for (int i = 0; i < height; i++) {
// NoBarrier_SetNext() suffices since we will add a barrier when
// we publish a pointer to "x" in prev[i].
x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));
prev[i]->SetNext(i, x);
}
}
** Contains **
是否包含key值的Node
template<typename Key, class Comparator>
bool SkipList<Key,Comparator>::Contains(const Key& key) const {
//调用函数
Node* x = FindGreaterOrEqual(key, nullptr);
//如果相等则返回true
if (x != nullptr && Equal(key, x->key)) {
return true;
} else {
return false;
}
}
总结
以上,我们分析了在levelDB中跳表的具体实现方法,我们可以注意到里面并没有删除操作。
结合跳表的原理和结构来看,这部分也非常好理解。