java实现跳表

前言

跳表是从链表演化过来的,对于链表来说,即使是已经排序的,也只能从头遍历,没办法像数组一样支持二分查找。那么有没有什么提高查找效率的方法呢?我们可以给链表建立索引,大概每4个节点抽取出一个索引节点,这种对链表添加多级索引的数据结构就是跳表,类似下图。

跳表的查找

假如我们要查找15节点,查找的节点顺序为1->7->14->14->14->15,只需要查找6次,相比于单链表的查找,效率要高很多,索引层数越多,效率越高,但相对应的空间使用也就越多,跳表就是使用空间换时间。

代码实现

import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;

/**
 * An implementation of {@link java.util.Map} based on skip lists, a data structure first described
 * in 1989 by William Pugh.
 * <p>
 * Note that this implementation is not thread-safe which means it can NOT be used without
 * synchronization.
 *
 * @author Robin Wang
 */
public class SkipListMap<K, V> extends AbstractMap<K, V> implements SortedMap<K, V>,
    java.io.Serializable {

  private static final long serialVersionUID = -7294702195698320197L;

  /**
   * The maximum level of the skip list.
   */
  private static final int MAX_LEVEL = 32;

  /**
   * The comparator used to maintain order in this skip list map, or null if it uses the natural
   * ordering of its keys.
   */
  private final Comparator<? super K> comparator;

  /**
   * The head of this skip list. This node is a sentinel node.
   */
  private final Node<K, V> head = new Node<>(null, null, MAX_LEVEL);

  /**
   * The tail node of this skip list. This node is a sentinel node like head node.
   */
  private final Node<K, V> tail = new Node<>(null, null, MAX_LEVEL);

  /**
   * Random for determining level of a skip list node.
   */
  private final Random random = new Random();

  /**
   * The current level of this skip list map.
   */
  private int level;

  /**
   * The size of this skip list map. This value should be changed after any modification like
   * insertion, removal, etc.
   */
  private int size;

  /**
   * The entry set view of this skip list map.
   */
  private transient EntrySet entrySet;

  /**
   * The key set view of this skip list map.
   */
  private transient KeySet keySet;

  /**
   * The value collection view of this skip list map.
   */
  private transient Values values;

  /**
   * Constructs a new, empty skip list map, using the natural ordering of its keys. All keys
   * inserted into the map must implement the {@link Comparable} interface. Furthermore, all such
   * keys must be
   * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
   * a {@code ClassCastException} for any keys {@code k1} and {@code k2} in the map. If the user
   * attempts to put a key into the map that violates this constraint (for example, the user
   * attempts to put a string key into a map whose keys are integers), the {@code put(Object key,
   * Object value)} call will throw a {@code ClassCastException}.
   */
  public SkipListMap() {
    this(null);
  }

  /**
   * Constructs a new, empty skip list map, ordered according to the given comparator.All keys
   * inserted into the map must be <em>mutually comparable</em> by the given comparator: {@code
   * comparator.compare(k1, k2)} must not throw a {@code ClassCastException} for any keys {@code k1}
   * and {@code k2} in the map. If the user attempts to put a key into the map that violates this
   * constraint, the {@code put(Object key, Object value)} call will throw a {@code
   * ClassCastException}.
   *
   * @param comparator the comparator that will be used to order this map. If {@code null}, the
   * {@linkplain Comparable natural ordering} of the keys will be used.
   */
  public SkipListMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
    for (int i = 0; i < MAX_LEVEL; i++) {
      linkNode(head, tail, i);
    }
  }

  /**
   * Returns the number of key-value mappings in this map.
   *
   * @return the number of key-value mappings in this map
   */
  @Override
  public int size() {
    return this.size;
  }

  /**
   * Returns <tt>true</tt> if this skip list map includes a mapping for the specified key.
   *
   * @param key key whose presence in this map is to be tested
   * @return <tt>true</tt> if this map includes a mapping for the specified
   * key
   * @throws ClassCastException if the key is of an inappropriate type for this map
   * @throws NullPointerException if the specified key is null and this map does not permit null
   * keys
   */
  @Override
  @SuppressWarnings("unchecked")
  public boolean containsKey(Object key) {
    return findClosestNode((K) key, Relation.EQ) != null;
  }

  /**
   * Returns the value to which the specified key is mapped, or {@code null} if this skip list map
   * includes no mapping for the key.
   *
   * @param key the key whose associated value is to be returned
   * @return the value to which the specified key is mapped, or {@code null} if this map includes no
   * mapping for the key
   * @throws ClassCastException if the key is of an inappropriate type for this map
   * @throws NullPointerException if the specified key is null and this map does not permit null
   * keys
   */
  @Override
  @SuppressWarnings("unchecked")
  public V get(Object key) {
    Node<K, V> node = findClosestNode((K) key, Relation.EQ);
    return node == null ? null : node.value;
  }

  /**
   * Associates the specified value with the specified key in this skip list map. If the map
   * previously contained a mapping for the key, the old value is replaced.
   *
   * @param key key with which the specified value is to be associated
   * @param value value to be associated with the specified key
   * @return the previous value associated with {@code key}, or {@code null} if there was no mapping
   * for {@code key}. (A {@code null} return can also indicate that the map previously associated
   * {@code null} with {@code key}.)
   * @throws ClassCastException if the specified key cannot be compared with the keys currently in
   * the map
   * @throws NullPointerException if the specified key is null and this map uses natural ordering,
   * or its comparator does not permit null keys
   */
  @Override
  @SuppressWarnings("unchecked")
  public V put(K key, V value) {
    Node<K, V>[] update = new Node[MAX_LEVEL];

    Node<K, V> node = findClosestNode(key, Relation.EQ, update);
    if (node != null) {
      V oldValue = node.value;
      node.value = value;
      return oldValue;
    }

    // insert a new node to this skip list map
    int newLevel = randomLevel();
    if (newLevel > level) {
      for (int i = level; i < newLevel; i++) {
        update[i] = head;
      }
      level = newLevel;
    }

    // create a new node and link it with existing nodes
    Node<K, V> newNode = new Node<>(key, value, newLevel);

    for (int i = 0; i < newLevel; i++) {
      // link new node and nodes with greater keys
      linkNode(newNode, update[i].next[i], i);

      // link new node and nodes with less keys
      linkNode(update[i], newNode, i);
    }
    size++;
    return null;
  }

  /**
   * Removes the mapping for a key from this skip list map if it is present (optional operation).
   * Returns the value to which this map previously associated the key, or <tt>null</tt> if the map
   * contained no mapping for the key.
   *
   * @param key key whose mapping is to be removed from the map
   * @return the previous value associated with <tt>key</tt>, or
   * <tt>null</tt> if there was no mapping for <tt>key</tt>.
   * @throws ClassCastException if the key is of an inappropriate type for this map
   * @throws NullPointerException if the specified key is null and this map does not permit null
   * keys
   */
  @Override
  @SuppressWarnings("unchecked")
  public V remove(Object key) {
    Node<K, V> node = findClosestNode((K) key, Relation.EQ);
    if (node != null) {
      deleteNode(node);
      return node.value;
    }
    return null;
  }

  /**
   * Removes all of the mappings from this skip list map. The skip list map will be empty after this
   * call returns.
   */
  @Override
  public void clear() {
    for (int i = 0; i < MAX_LEVEL; i++) {
      Node<K, V> node = head;
      // help GC
      while (node != null) {
        Node<K, V> next = node.next[i];
        node.next[i] = null;
        node = next;
      }
    }
    size = 0;
  }

  /**
   * Returns a {@link Set} view of the mappings contained in this skip list map.
   *
   * @return a set view of the mappings contained in this skip list map, sorted in ascending key
   * order
   */
  @Override
  public Set<Entry<K, V>> entrySet() {
    return entrySet == null ? entrySet = new EntrySet() : entrySet;
  }

  /**
   * Returns a {@link Set} view of keys contained in this skip list map. The result contains all
   * keys in ascending order corresponding to the given comparator or natural order by default.
   *
   * @return a set view of keys contained in this skip list map.
   */
  @Override
  public Set<K> keySet() {
    return keySet == null ? keySet = new KeySet() : keySet;
  }

  /**
   * Returns a collection view of all values contained in this skip list map. The values are given
   * in ascending order corresponding to the given comparator or natural order by default.
   *
   * @return a collection view of values contained in this skip list map.
   */
  @Override
  public Collection<V> values() {
    return values == null ? values = new Values() : values;
  }

  /**
   * Returns the comparator used to order the keys in this skip list map, or {@code null} if this
   * map uses the {@linkplain Comparable natural ordering} of its keys.
   *
   * @return the comparator used to order the keys in this skip list map, or {@code null} if this
   * map uses the natural ordering of its keys
   */
  @Override
  public Comparator<? super K> comparator() {
    return comparator;
  }

  /**
   * Returns a view of the portion of this map whose keys range from {@code fromKey}, inclusive, to
   * {@code toKey}, exclusive.  (If {@code fromKey} and {@code toKey} are equal, the returned map is
   * empty.)
   * <p>
   *
   * @param fromKey low endpoint (inclusive) of the keys in the returned map
   * @param toKey high endpoint (exclusive) of the keys in the returned map
   * @return a view of the portion of this map whose keys range from {@code fromKey}, inclusive, to
   * {@code toKey}, exclusive
   * @throws IllegalArgumentException if {@code fromKey} is greater than {@code toKey}; or if this
   * map itself has a restricted range, and {@code fromKey} or {@code toKey} lies outside the bounds
   * of the range
   */

  @Override
  public SortedMap<K, V> subMap(K fromKey, K toKey) {
    return new SortedSubMap<>(this, false, fromKey, false, toKey);
  }

  /**
   * Returns a view of the portion of this map whose keys are strictly less than {@code toKey}.
   *
   * @param toKey high endpoint (exclusive) of the keys in the returned map
   * @return a view of the portion of this map whose keys are strictly less than {@code toKey}
   * @throws IllegalArgumentException if this map itself has a restricted range, and {@code toKey}
   * lies outside the bounds of the range
   */
  @Override
  public SortedMap<K, V> headMap(K toKey) {
    return new SortedSubMap<>(this, true, null, false, toKey);
  }

  /**
   * Returns a view of the portion of this map whose keys are greater than or equal to {@code
   * fromKey}.
   *
   * @param fromKey low endpoint (inclusive) of the keys in the returned map
   * @return a view of the portion of this map whose keys are greater than or equal to {@code
   * fromKey}
   * @throws IllegalArgumentException if this map itself has a restricted range, and {@code fromKey}
   * lies outside the bounds of the range
   */
  @Override
  public SortedMap<K, V> tailMap(K fromKey) {
    return new SortedSubMap<>(this, false, fromKey, true, null);
  }

  /**
   * Returns the first (lowest) key currently in this skip list map.
   *
   * @return the first (lowest) key currently in this skip list map
   * @throws NoSuchElementException if this map is empty
   */
  @Override
  public K firstKey() {
    Node<K, V> firstNode = firstNode();
    if (firstNode == null) {
      throw new NoSuchElementException();
    }
    return firstNode.key;
  }

  /**
   * Returns the last (highest) key currently in this skip list map.
   *
   * @return the last (highest) key currently in this skip list map
   * @throws NoSuchElementException if this map is empty
   */
  @Override
  public K lastKey() {
    Node<K, V> lastNode = lastNode();
    if (lastNode == null) {
      throw new NoSuchElementException();
    }
    return lastNode.key;
  }

  private Node<K, V> firstNode() {
    return dataNodeOrNull(head.next[0]);
  }

  private Node<K, V> lastNode() {
    return dataNodeOrNull(tail.prev[0]);
  }

  @SuppressWarnings("unchecked")
  private int compare(Comparator comparator, Node node, K key) {
    // special judge for head/tail
    if (node == head || node == tail) {
      return node == head ? -1 : 1;
    }
    return (comparator != null) ? comparator.compare(node.key, key)
        : ((Comparable<? super K>) node.key)
            .compareTo(key);
  }

  @SuppressWarnings("unchecked")
  // compare two keys
  final int compare(K lhs, K rhs) {
    return comparator != null ? comparator.compare(lhs, rhs)
        : ((Comparable<? super K>) lhs).compareTo(rhs);
  }

  private boolean isDataNode(Node<K, V> node) {
    return node != null && node != head && node != tail;
  }

  private Node<K, V> dataNodeOrNull(Node<K, V> node) {
    return isDataNode(node) ? node : null;
  }

  private boolean checkEquality(Object key, Node<K, V> node) {
    return isDataNode(node) && Objects.equals(node.key, key);
  }

  private void deleteNode(Node<K, V> node) {
    if (node == null) {
      return;
    }

    for (int i = 0; i < node.next.length; i++) {
      linkNode(node.prev[i], node.next[i], i);

      node.prev[i] = null;
      node.next[i] = null;
    }
    while (level > 0 && head.next[level] == null) {
      level--;
    }
    size--;
  }

  private void linkNode(Node<K, V> prevNode, Node<K, V> nextNode, int level) {
    if (prevNode != null) {
      prevNode.next[level] = nextNode;
    }
    if (nextNode != null) {
      nextNode.prev[level] = prevNode;
    }
  }

  private Node<K, V> findClosestNode(K key, Relation relation) {
    return findClosestNode(key, relation, null);
  }

  /**
   * Find the node whose key is closest to the given key corresponding to the given {@link
   * Relation}.
   *
   * @param key the specific key used to find the closest node
   * @param relation the specific relation
   * @param update the update array which is used by {@link SkipListMap#put(Object, Object)}
   * @return the node whose key is closest to the given {@code key} or null if there is not.
   */
  private Node<K, V> findClosestNode(K key, Relation relation, Node<K, V>[] update) {
    Node<K, V> node = head;
    for (int i = level - 1; i >= 0; i--) {
      while (i < node.next.length
          && node.next[i] != null
          && compare(comparator, node.next[i], key) < 0) {
        node = node.next[i];
      }
      if (update != null) {
        update[i] = node;
      }
    }
    if (relation.includes(Relation.GT)) {
      return dataNodeOrNull(node.next[0]);
    }
    if (relation == Relation.LT) {
      return dataNodeOrNull(node);
    }
    if (relation == Relation.EQ) {
      return checkEquality(key, node.next[0]) ? node.next[0] : null;
    }
    // LE
    return checkEquality(key, node.next[0]) ? node.next[0] : dataNodeOrNull(node);
  }

  private int randomLevel() {
    // the following implementation is basically as same as Redis ZSET implementation
    // see https://github.com/antirez/redis/blob/4.0/src/t_zset.c

    int newLevel = 1;
    while ((random.nextInt() & 0xFFFF) < (0xFFFF >> 2)) { //25%
      newLevel++;
    }
    return (newLevel < MAX_LEVEL) ? newLevel : MAX_LEVEL;
  }

  private EntryIterator entryIterator() {
    return new EntryIterator(firstNode());
  }

  private Iterator<K> keyIterator() {
    return new KeyIterator(entryIterator());
  }

  private Iterator<V> valueIterator() {
    return new ValueIterator(entryIterator());
  }

  private class EntryIterator implements Iterator<Entry<K, V>> {

    Node<K, V> nextNode;
    Entry<K, V> prevEntry;

    EntryIterator(Node<K, V> first) {
      this.nextNode = first;
    }

    private Entry<K, V> prevEntry() {
      if (!hasNext()) {
        throw new NoSuchElementException();
      }
      prevEntry = this.nextNode;
      nextNode = this.nextNode.prev[0];

      return prevEntry;
    }

    @Override
    public boolean hasNext() {
      return nextNode != null && nextNode != tail;
    }

    @Override
    public Entry<K, V> next() {
      return nextEntry();
    }

    private Entry<K, V> nextEntry() {
      if (!hasNext()) {
        throw new NoSuchElementException();
      }
      prevEntry = this.nextNode;
      nextNode = this.nextNode.next[0];

      return prevEntry;
    }
  }

  /**
   * Internal skip list node to store key-value pair.
   */
  @SuppressWarnings("unchecked")
  private static final class Node<K, V> implements Entry<K, V> {

    final K key;
    V value;
    final Node<K, V>[] next;
    final Node<K, V>[] prev;

    Node(K key, V value, int level) {
      this.key = key;
      this.value = value;
      this.next = new Node[level];
      this.prev = new Node[level];
    }

    @Override
    public K getKey() {
      return key;
    }

    @Override
    public V getValue() {
      return value;
    }

    @Override
    public V setValue(V value) {
      V oldValue = this.value;
      this.value = value;
      return oldValue;
    }

    @Override
    public final String toString() {
      return key + "=" + value;
    }
  }

  /**
   * Relation enum for searching nodes.
   */
  private enum Relation {
    EQ(1),
    GT(2),
    GE(EQ.value | GT.value),
    LT(4),
    LE(EQ.value | LT.value);

    private final int value;

    Relation(int value) {
      this.value = value;
    }

    boolean includes(Relation other) {
      return (value & other.value) != 0;
    }
  }

  private class KeyIterator implements Iterator<K> {

    private final EntryIterator entryIterator;

    KeyIterator(EntryIterator entryIterator) {
      this.entryIterator = entryIterator;
    }

    @Override
    public boolean hasNext() {
      return entryIterator.hasNext();
    }

    @Override
    public K next() {
      return entryIterator.next().getKey();
    }
  }

  private class ValueIterator implements Iterator<V> {

    private final EntryIterator entryIterator;

    ValueIterator(EntryIterator entryIterator) {
      this.entryIterator = entryIterator;
    }

    @Override
    public boolean hasNext() {
      return entryIterator.hasNext();
    }

    @Override
    public V next() {
      return entryIterator.next().getValue();
    }
  }

  private class EntrySet extends AbstractSet<Entry<K, V>> {

    @Override
    public Iterator<Entry<K, V>> iterator() {
      return entryIterator();
    }

    @Override
    public int size() {
      return SkipListMap.this.size();
    }
  }

  private class KeySet extends AbstractSet<K> {

    @Override
    public Iterator<K> iterator() {
      return keyIterator();
    }

    @Override
    public int size() {
      return size;
    }
  }

  private class Values extends AbstractCollection<V> {

    @Override
    public Iterator<V> iterator() {
      return valueIterator();
    }

    @Override
    public int size() {
      return size;
    }
  }

  private static class SortedSubMap<K, V> extends AbstractMap<K, V> implements SortedMap<K, V>,
      java.io.Serializable {

    private static final long serialVersionUID = -7244440549791596855L;

    final SkipListMap<K, V> map;

    final boolean fromStart;

    final boolean toEnd;

    // inclusive
    final K low;

    // exclusive
    final K high;

    transient EntrySet entrySet;

    SortedSubMap(SkipListMap<K, V> map, boolean fromStart, K low, boolean toEnd, K high) {
      if (!fromStart && !toEnd) {
        if (map.compare(low, high) > 0) {
          throw new IllegalArgumentException("fromKey > toKey");
        }
      }
      this.map = map;
      this.low = low;
      this.high = high;
      this.fromStart = fromStart;
      this.toEnd = toEnd;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
      return entrySet == null ? entrySet = new EntrySet() : entrySet;
    }

    @Override
    public Comparator<? super K> comparator() {
      return map.comparator();
    }

    @Override
    public SortedMap<K, V> subMap(K fromKey, K toKey) {
      if (!inRange(fromKey) || !inRange(toKey)) {
        throw new IllegalArgumentException("out of range");
      }
      return new SortedSubMap<>(map, fromStart, fromKey, toEnd, toKey);
    }

    @Override
    public SortedMap<K, V> headMap(K toKey) {
      if (!inRange(toKey)) {
        throw new IllegalArgumentException("out of range");
      }
      return new SortedSubMap<>(map, fromStart, low, toEnd, toKey);
    }

    @Override
    public SortedMap<K, V> tailMap(K fromKey) {
      if (!inRange(fromKey)) {
        throw new IllegalArgumentException("out of range");
      }
      return new SortedSubMap<>(map, fromStart, fromKey, toEnd, high);
    }

    @Override
    public K firstKey() {
      return checkKey(lowestNode());
    }

    @Override
    public K lastKey() {
      return checkKey(highestNode());
    }

    // first or the one >= low
    Node<K, V> lowestNode() {
      return fromStart ? map.firstNode() : map.findClosestNode(low, Relation.GE);
    }

    // last or the one that < high
    Node<K, V> highestNode() {
      return toEnd ? map.lastNode() : map.findClosestNode(high, Relation.LT);
    }

    final boolean inRange(K key) {
      // check if in closed range
      return (fromStart || map.compare(key, low) >= 0)
          && (toEnd || map.compare(key, high) <= 0);
    }

    private K checkKey(Node<K, V> entry) {
      if (entry == null) {
        throw new NoSuchElementException();
      }
      return entry.getKey();
    }

    private class EntrySet extends AbstractSet<Entry<K, V>> {

      transient int size = -1;

      @Override
      public Iterator<Entry<K, V>> iterator() {
        return new EntryIterator(lowestNode(), highestNode());
      }

      @Override
      public int size() {
        if (fromStart && toEnd) {
          return map.size();
        }
        if (size == -1) {
          size = 0;
          forEach(ignored -> size++);
        }
        return size;
      }

      @Override
      public boolean isEmpty() {
        return lowestNode() != null;
      }

      private class EntryIterator implements Iterator<Entry<K, V>> {

        Node<K, V> nextNode;

        // exclusive
        final Node<K, V> bound;

        Entry<K, V> prevEntry;

        EntryIterator(Node<K, V> fromNode, Node<K, V> bound) {
          this.nextNode = fromNode;
          this.bound = bound;
        }

        @Override
        public boolean hasNext() {
          return nextNode != null && prevEntry != bound;
        }

        @Override
        public Entry<K, V> next() {
          if (!hasNext()) {
            throw new NoSuchElementException();
          }
          prevEntry = this.nextNode;
          nextNode = this.nextNode.next[0];
          return prevEntry;
        }
      }

    }
  }
}

代码参考github实现https://github.com/MottoX/SkipList
Redis的有序集合实现就使用到了跳表,java并发包的ConcurrentSkipListMap也是跳表的实现。

参考

数据结构与算法——跳表
Redis源码学习之跳表
github开源实现

posted @ 2021-04-30 23:20  strongmore  阅读(166)  评论(0编辑  收藏  举报