Data structure-jump table (based on JAVA implementation)

The principle and characteristics of skip meter

The essence of the skip table is an ordered linked list that can perform binary search. The skip table adds a multi-level index to the original ordered linked list, and fast search is realized through the index. It solves the problem of slow query of linked list, but it will occupy more memory. It is a data structure that exchanges space for memory.

The time complexity of querying arbitrary data O(logn)
The time complexity of inserting data O(logn)
Space complexity: O(n)

The data structure of the jump table

Jump table application scenario analysis

On the server side, when there are requirements for concurrency and performance, how to choose the appropriate data structure (jump table and red-black tree)?
If you simply compare the performance, the jump table and the red-black tree can be said to be the same, but the concurrency environment is different. If you want to update the data, the jump table needs to be updated less, and there are fewer locks. , So the cost of different threads competing for locks is relatively small. The red-black tree has a balanced process, involving a large number of nodes, and the cost of contention for locks is relatively high. The performance is not as good as the former.

In a concurrent environment, Skip List has another advantage. Red-black trees may need to do some rebalance operations when inserting and deleting. Such operations may involve other parts of the entire tree, and Skip List operations are obviously more local. For some, there are fewer nodes that the lock needs to focus on, so the performance is better in this case.

In Redis, the sorted set data type (Sorted Set) is also implemented with a jump table.
Redis author described the reasons for using jump tables:

1. One disadvantage of the jump table is memory consumption (because of repeated hierarchical storage of nodes), but the author also said that the parameters can be adjusted to reduce memory consumption, which is similar to those of the balanced tree structure.
2. Redis has a range of operations after investigation, so that it can be easily operated by using the doubly linked list in the jump table. In addition, cache locality is not worse than balanced trees.
3. Simple to implement. The zrank operation can reach O(log(N)).

JAVA Application of Jump Table

It has been implemented in the Java API:

  1. ConcurrentSkipListMap. Corresponds to HashTable, HashMap, TreeMap in function.
  2. ConcurrentSkipListSet. Functionally corresponds to HashSet. To be
    precise, Skip List is more like TreeMap in Java. TreeMap is implemented based on a red-black tree (a self-balancing binary search tree). The average time complexity can reach O(log n). The output of TreeMap is ordered, and the output of ConcurrentSkipListMap and ConcurrentSkipListSet is also ordered (tested by this blog) ). The output of the following example is from small to large, orderly.

Use example

import java.util.*;
import java.util.concurrent.*;
/*
 * 跳表(SkipList)这种数据结构算是以前比较少听说过,它所实现的功能与红黑树,AVL树都差不太多,说白了就是一种基于排序的索引结构,
 * 它的统计效率与红黑树差不多,但是它的原理,实现难度以及编程难度要比红黑树简单。 
 * 另外它还有一个平衡的树形索引机构没有的好处,这也是引导自己了解跳表这种数据结构的原因,就是在并发环境下其表现很好. 
 * 这里可以想象,在没有了解SkipList这种数据结构之前,如果要在并发环境下构造基于排序的索引结构,那么也就红黑树是一种比较好的选择了,
 * 但是它的平衡操作要求对整个树形结构的锁定,因此在并发环境下性能和伸缩性并不好.
 * 在Java中,skiplist提供了两种:
 * ConcurrentSkipListMap 和 ConcurrentSkipListSet 
 * 两者都是按自然排序输出。
 */
public class SkipListDemo {
    
    	
	public static void skipListMapShow(){
    
    
	Map<Integer,String> map= new ConcurrentSkipListMap<>();
	map.put(1, "1");
	map.put(23, "23");
	map.put(3, "3");
	map.put(2, "2");
	for(Integer key : map.keySet()){
    
    
		System.out.println(map.get(key));
	 }
	}
	
	public static void skipListSetShow(){
    
    
		Set<Integer> mset= new ConcurrentSkipListSet<>();
		mset.add(1);
		mset.add(21);
		mset.add(6);
		mset.add(2);
		System.out.println("ConcurrentSkipListSet result="+mset);
		
		Set<String> myset = new ConcurrentSkipListSet<>();
		System.out.println(myset.add("abc"));
		System.out.println(myset.add("fgi"));
		System.out.println(myset.add("def"));
		System.out.println(myset.add("Abc"));
		System.out.println("ConcurrentSkipListSet contains="+myset);
		}
}

Output result:

1
2
3
23

ConcurrentSkipListSet result=[1, 2, 6, 21]

true
true
true
true
ConcurrentSkipListSet contains=[Abc, abc, def, fgi]

Source code analysis

ConcurrentSkipListMap

ConcurrentSkipListMap is similar to TreeMap, although they are both ordered hash tables. However, first, their thread safety mechanisms are different. TreeMap is non-thread-safe, while ConcurrentSkipListMap is thread-safe. Second, ConcurrentSkipListMap is implemented by jumping the table, while TreeMap is implemented by red-black trees.

The data structure of ConcurrentSkipListMap is shown in the following figure: The
Data structure of ConcurrentSkipListMapskip table is divided into many levels, and each layer can be regarded as an index of data. The meaning of these indexes is to speed up the speed of searching data in the skip table. The data of each layer is ordered, the data of the upper layer is a subset of the data of the next layer, and the first layer (level 1) contains all the data; the higher the level, the greater the jump, and the data contained Less. The jump table contains a header. When searching for data, it searches from top to bottom and from left to right.

First take the data "7,14,21,32,37,71,85" sequence as an example to briefly explain the jump table. Find the "32" node path in the jump table as shown in the figure below:
Example

public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentNavigableMap<K,V>,
               Cloneable,
               java.io.Serializable {
    
    
    //head是跳表的表头
    private transient volatile HeadIndex<K,V> head;
}

static class Index<K,V> {
    
    
    final Node<K,V> node;       //哈希表节点node
    final Index<K,V> down;      //下索引的指针
    volatile Index<K,V> right;  //右索引的指针
}

static final class HeadIndex<K,V> extends Index<K,V> {
    
    
    final int level;    //节点所属层次
    HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
    
    
        super(node, down, right);
        this.level = level;
    }
}

static final class Node<K,V> {
    
    
    final K key;
    volatile Object value;
    volatile Node<K,V> next;
}

ConcurrentSkipListSet

ConcurrentSkipListSet is implemented through ConcurrentSkipListMap. It contains a ConcurrentNavigableMap object m, which is actually the implementation class of ConcurrentNavigableMap ConcurrentSkipListMap. It only uses the key in ConcurrentSkipListMap, and its value is an empty Object.

public class ConcurrentSkipListSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable {
    
    
    private final ConcurrentNavigableMap<E,Object> m;

    public ConcurrentSkipListSet() {
    
    
        m = new ConcurrentSkipListMap<E,Object>();
    }
}

Guess you like

Origin blog.csdn.net/u014134750/article/details/103549166