Java中的跳跃表
Java API中提供了支持并发操作的跳跃表ConcurrentSkipListSet和ConcurrentSkipListMap。下面摘录”Java多线程(四)之ConcurrentSkipListMap深入分析“中的一些结论。
有序的情况下:
1. 在非多线程的情况下,应当尽量使用TreeMap(红黑树实现)。
2. 对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率。但是对于高并发程序,应当使用ConcurrentSkipListMap。
无序情况下:
3. 并发程度低,数据量大时,ConcurrentHashMap 存取远大于ConcurrentSkipListMap。
4. 数据量一定,并发程度高时,ConcurrentSkipListMap比ConcurrentHashMap效率更高。
算法参考
增删查改的时间复杂度都与高度相关(0(logn))
实现代码
package org.kelab.skiplist;
import lombok.Data;
/**
* Created by hongfei.whf on 2017/5/10.
*/
@Data
public class SkipListNode<T> {
public static final int HEAD_KEY = Integer.MAX_VALUE;
public static final int TAIL_KEY = Integer.MAX_VALUE;
private int key;
private T value;
private SkipListNode<T> up, down, left, right;
public SkipListNode(int key, T value) {
this.key = key;
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SkipListNode)) return false;
if (!super.equals(o)) return false;
SkipListNode<?> that = (SkipListNode<?>) o;
if (key != that.key) return false;
return value != null ? value.equals(that.value) : that.value == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + key;
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
@Override
public String toString() {
return key + "-" + value;
}
}
package org.kelab.skiplist;
import lombok.Data;
import java.util.Random;
/**
* Created by hongfei.whf on 2017/5/10.
*/
@Data
public class SkipList<T> {
private SkipListNode<T> head, tail;
private int nodes;
private int levels;
private Random rand;
private static final double PROBABILITY = 0.5;
/**
* 构建跳跃表
*/
public SkipList() {
this.rand = new Random();
clear();
}
/**
* 是否为空跳跃表
*
* @return
*/
public boolean isEmpty() {
return nodes == 0;
}
/**
* 跳跃表大小
*
* @return
*/
public int size() {
return nodes;
}
/**
* 清空跳跃表
*/
public void clear() {
this.head = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
this.tail = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
this.levels = this.nodes = 0;
left2right(this.head, this.tail);
}
/**
* 左->右连接
*
* @param node1
* @param node2
*/
public void left2right(SkipListNode<T> node1, SkipListNode<T> node2) {
node1.setRight(node2);
node2.setLeft(node1);
}
/**
* 上->下连接
*
* @param node1
* @param node2
*/
public void up2down(SkipListNode<T> node1, SkipListNode<T> node2) {
node1.setDown(node2);
node2.setUp(node1);
}
/**
* 查找是否存在key结点,若存在返回value,
* 否则返回前一个位置的节点
*
* @param key
* @return
*/
private SkipListNode<T> find(int key) {
SkipListNode<T> p = this.head;
while (true) {
while (p.getRight().getKey() != SkipListNode.TAIL_KEY
&& p.getRight().getKey() <= key) {
p = p.getRight();
}
if (p.getDown() != null) {
p = p.getDown();
} else {
break;
}
}
return p;
}
/**
* 查询是否存在key,存在返回value,否则返回null
*
* @param key
* @return
*/
public SkipListNode<T> search(int key) {
SkipListNode<T> p = this.find(key);
if (p.getKey() == key) {
return p;
} else {
return null;
}
}
/**
* 跳跃表插入操作
*
* @param key
* @param value
*/
public void put(int key, T value) {
SkipListNode<T> p = find(key);
if (p.getKey() == key) {
// 替换最底层节点
p.setValue(value);
// 如果存在上层节点,一样替换
SkipListNode up = p.getUp();
while (up != null) {
up.setValue(value);
up = up.getUp();
}
return;
}
SkipListNode q = new SkipListNode(key, value);
append(p, q);
int currLevel = 0;
// 概率1/2:是否往上加节点
while (this.rand.nextDouble() < PROBABILITY) {
// 如果超出高度,新建一层
if (currLevel >= this.levels) {
this.levels++;
// 新建一层
SkipListNode<T> p1 = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
SkipListNode<T> q1 = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
left2right(p1, q1);
// 连接顶层和新建的一层
up2down(p1, this.head);
up2down(q1, this.tail);
this.head = p1;
this.tail = q1;
}
// 将p移到上层节点
while (p.getUp() == null) {
p = p.getLeft();
}
p = p.getUp();
// 在上层比起小的节点后面,添加节点
SkipListNode<T> e = new SkipListNode<>(key, value);
append(p, e);
up2down(e, q);
q = e;
currLevel++;
}
nodes++;
}
/**
* 跳跃表删除操作
*
* @param key
*/
public void remove(int key) {
SkipListNode<T> p = find(key);
if (p.getKey() == key) {
// 删除最底层及其上面的结点
SkipListNode up = p.getUp();
remove(p);
while (up != null) {
remove(up);
up = up.getUp();
}
nodes--;
// 清除空层
while (head.getRight().getKey() == SkipListNode.TAIL_KEY) {
head = head.getDown();
levels--;
}
return;
}
}
/**
* node1 后面紧跟着插入node2
* node<->node3 ==> node<->node2<->node3
*
* @param node1
* @param node2
*/
private void append(SkipListNode node1, SkipListNode node2) {
node2.setLeft(node1);
node2.setRight(node1.getRight()/*node3*/);
node1.getRight()/*node3*/.setLeft(node2);
node1.setRight(node2);
}
/**
* 删除node节点
* node1<->node<->node2 ==> node1<->node2
*
* @param node
*/
private void remove(SkipListNode node) {
SkipListNode node1 = node.getLeft();
SkipListNode node2 = node.getRight();
left2right(node1, node2);
}
/**
* 打印出原始数据
*/
@Override
public String toString() {
if (isEmpty()) {
return "跳跃表为空!";
}
StringBuilder builder = new StringBuilder();
SkipListNode<T> p = head;
int plevels = levels;
while (plevels-- >= 0) {
SkipListNode<T> q = p.getDown();
boolean isFirst = true;
while (p.getRight() != null) {
if (isFirst) {
isFirst = false;
} else {
builder.append(" <=> ");
}
builder.append(p.toString());
p = p.getRight();
}
builder.append("\n");
p = q;
}
return builder.toString();
}
public static void main(String[] args) {
SkipList<String> list = new SkipList<String>();
System.out.println(list);
list.put(2, "a");
list.put(1, "b");
list.put(3, "c");
list.put(1, "d");//测试同一个key值
System.out.println(list.getLevels());
System.out.println(list);
System.out.println("====");
list.remove(2);
System.out.println(list);
System.out.println(list.size());
}
}