zoukankan      html  css  js  c++  java
  • 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开源实现

  • 相关阅读:
    go开发环境配置
    Go环境配置
    为什么Redis集群有16384个槽【转发】
    Spring Cloud Hystrix降级处理超时时间设置
    win10下查看进程,杀死进程【转载】
    SpringCloud的各种超时时间配置效果
    解决springcloud Feign项目中遇到的timeout请求超时的问题【转载】
    数据库索引
    XML
    JDBC
  • 原文地址:https://www.cnblogs.com/strongmore/p/14723596.html
Copyright © 2011-2022 走看看