SkipListNode -- 跳跃表节点
package skiplist; @Data public class SkipListNode<T> { //索引层数组 private SkipListLevel[] level; //后退所指向的节点 private SkipListNode<T> backword; //分值 private double score; //成员对象 private T obj; //初始化头节点时用到的构造方法 SkipListNode(T obj) { this.obj = obj; //redis要求索引层数最高是32层 this.level = new SkipListLevel[32]; initLevel(this.level, 32); this.score = 0; } //根据"层高"和"分值"新建一个节点,在插入节点时使用 SkipListNode(T obj, int levelHeight, double score) { this.obj = obj; this.level = new SkipListLevel[levelHeight]; initLevel(this.level, levelHeight); this.score = score; } //初始化每一层 private void initLevel(SkipListLevel[] level, int height) { for (int i = 0; i < height; ++i) { level[i] = new SkipListLevel(); } } }2.SkipListLevel -- 索引层对象
package skiplist; import lombok.Data; @Data public class SkipListLevel { //指向下一个节点 private SkipListNode forward; /** * 跨度,当前节点和下一个节点间的距离 * 如果下一个节点为null,当前节点为头节点或尾节点时,跨度为0,否则跨度不为0 */ private int span; }3. SkipList -- 跳跃表
package skiplist; import java.util.Random; public class SkipList <T extends Comparable<? super T>> { //头节点 private SkipListNode<T> header; //尾结点 private SkipListNode<T> tail; //跳跃表中节点的数量,(头节点不算) private int length; //当前跳跃表最高的层数,(头节点不算,头节点的索引层高固定为32) private int maxLevel; //构造方法初始化SkipList public SkipList() { //初始化头节点 SkipListNode<T> node = new SkipListNode<>(null); this.header = node; this.tail = node; this.length = 0; this.maxLevel = 0; } }对于新节点的索引层高,redis使 用 了 幂次定律 来生成,幂次定律是一种理论,并不是代码实现,所以是否生成下一层的概率也不是固定的,redis使用的概率为1/4
/** * 获取随机的层高度 **/ private int getRandomHeight() { Random random = new Random(); int i = 1; for (; i < 32; ++i) { if (random.nextInt(4) == 0) { break; } } return i; }插入节点代码如下
public SkipListNode slInsert(double score, T obj) { int i; //存放待修改的节点 SkipListNode[] update = new SkipListNode[32]; //存放待修改的节点到头节点的距离 int[] rank = new int[32]; //当前节点 SkipListNode node = header; //新插入节点的索引层高度 int level = getRandomHeight(); //从上往下,从左往右先找到要修改的节点 for(i = maxLevel -1 ;i>=0;--i){ //如果左侧已经数好距离,则直接使用,减少数距离的次数 rank[i] = i==(maxLevel-1)? 0 : rank[i+1]; SkipListNode<T> next = node.getLevel()[i].getForward(); //向右一个一个比较,找到最后一个比待插入节点分数小的节点,作为修改节点 while(next!=null && (score> next.getScore() || (score == next.getScore() && next.getObj().compareTo(obj) <0))){ rank[i]+= node.getLevel()[i].getSpan(); node = next; next = node.getLevel()[i].getForward(); } update[i] = node; } //找到新插入节点索引层高比原先节点索引层 高的部分要修改的节点 if(level > maxLevel){ for(i =maxLevel;i<level;++i){ //比原先高的层前一个节点,一定是头节点,span=0,forward = null rank[i] = 0; update[i] = header; //写入头节点到尾结点真实的距离 update[i].getLevel()[i].setSpan(length); } maxLevel = level; } SkipListNode<T> target = new SkipListNode<>(obj, level, score); for (i = 0; i < level; i++) { //新插入左侧节点的下一个节点就是新插入节点的下一个节点 target.getLevel()[i].setForward(update[i].getLevel()[i].getForward()); //新插入节点变成前一个节点的下一个节点 update[i].getLevel()[i].setForward(target); target.getLevel()[i].setSpan(update[i].getLevel()[i].getSpan() - (rank[0] - rank[i])); update[i].getLevel()[i].setSpan((rank[0] - rank[i]) + 1); } for (i = level; i < maxLevel; i++) { update[i].getLevel()[i].setSpan(update[i].getLevel()[i].getSpan() + 1); } //处理后置节点 target.setBackword(update[0] == header ? null : update[0]); if(target.getLevel()[0].getForward()!=null){ target.getLevel()[0].getForward().setBackword(target); }else{ tail = target; } length++; return target; }测试代码
public static void main(String[] args) { SkipList<String> list= init(); sout(list); list.slInsert2(4.0,"第4个元素"); System.out.println("____________________________________________________________________________________________________________________________________"); sout(list); } public static SkipList init(){ SkipList<String> list = new SkipList<>(); list.slInsert2(1.0,"第1个元素"); list.slInsert2(2.0,"第2个元素"); list.slInsert2(3.0,"第3个元素"); list.slInsert2(5.0,"第5个元素"); list.slInsert2(6.0,"第6个元素"); list.slInsert2(7.0,"第7个元素"); return list; } public static void sout(SkipList list){ for(int y=10 ;y>=0;y--) { SkipListNode node = list.header; for (; ; node = node.getLevel()[0].getForward()) { if (node != null) { if (y >= node.getLevel().length) { System.out.print(" |"); } else { System.out.print(" " + node.getScore() + ",span="+node.getLevel()[y].getSpan()+","+(node.getLevel()[y].getForward()==null?null:"next")+" |"); } } else { System.out.println(); break; } } } }