데이터 구조 점프 테이블 (JAVA 구현 기반)

스킵 미터의 원리와 특징

스킵 테이블의 본질은 바이너리 검색을 수행 할 수있는 정렬 된 링크리스트인데, 스킵 테이블은 원래의 정렬 된 링크리스트에 다단계 인덱스를 추가하여 인덱스를 통해 빠른 검색을 실현합니다. 연결된리스트의 느린 쿼리 문제를 해결하지만 더 많은 메모리를 차지하게되며, 메모리 공간을 교환하는 데이터 구조입니다.

임의 데이터 쿼리 시간 복잡도 O (logn)
데이터 삽입 시간 복잡도 O (logn)
공간 복잡도 : O (n)

점프 테이블의 데이터 구조

점프 테이블 적용 시나리오 분석

서버 측에서 동시성과 성능에 대한 요구 사항이있을 때 적절한 데이터 구조 (점프 테이블 및 레드-블랙 트리)를 선택하는 방법은 무엇입니까?
단순히 성능을 비교하면 점프 테이블과 레드-블랙 트리는 동일하다고 할 수 있지만 동시성 환경은 다릅니다. 데이터를 업데이트하려는 경우 점프 테이블의 업데이트가 적고 잠금이 더 적습니다. , 따라서 잠금을 위해 경쟁하는 서로 다른 스레드의 비용은 상대적으로 적습니다. 레드-블랙 트리는 많은 수의 노드를 포함하는 균형 잡힌 프로세스를 가지고 있으며 잠금에 대한 경합 비용이 상대적으로 높습니다. 성능은 이전만큼 좋지 않습니다.

동시 환경에서 Skip List는 또 다른 장점이 있습니다. Red-black 트리는 삽입 및 삭제시 일부 재조정 작업을 수행해야 할 수 있습니다. 이러한 작업은 전체 트리의 다른 부분을 포함 할 수 있으며 Skip List 작업은 분명히 더 로컬입니다. 일부의 경우 잠금이 집중해야하는 노드가 적으므로이 경우 성능이 더 좋습니다.

Redis에서는 정렬 된 세트 데이터 유형 (Sorted Set)도 점프 테이블로 구현됩니다.
Redis 작성자는 점프 테이블을 사용하는 이유를 설명했습니다.

1. 점프 테이블의 한 가지 단점은 메모리 소비 (노드의 계층 적 반복 저장으로 인한)이지만, 저자는 또한 균형 잡힌 트리 구조와 유사한 메모리 소비를 줄이기 위해 매개 변수를 조정할 수 있다고 말했습니다.
2. Redis는 조사 후 다양한 작업을 수행하므로 점프 테이블에서 이중 연결 목록을 사용하여 쉽게 작업 할 수 있습니다. 또한 캐시 지역 성은 균형 잡힌 트리보다 나쁘지 않습니다.
3. 구현이 간단합니다. zrank 연산은 O (log (N))에 도달 할 수 있습니다.

점프 테이블의 JAVA 응용

Java API에서 구현되었습니다.

  1. ConcurrentSkipListMap. 함수에서 HashTable, HashMap, TreeMap에 해당합니다.
  2. ConcurrentSkipListSet. 기능적으로 HashSet에 해당합니다.
    정확히 말하면 Skip List는 Java의 TreeMap과 비슷합니다. TreeMap은 red-black 트리 (자체 균형 이진 검색 트리)를 기반으로 구현됩니다. 평균 시간 복잡도는 O (log n)에 도달 할 수 있습니다. TreeMap의 출력이 정렬되고 ConcurrentSkipListMap 및 ConcurrentSkipListSet의 출력도 정렬됩니다 (이 블로그에서 테스트 됨). ). 다음 예제의 출력은 작은 순서에서 큰 순서로 나열됩니다.

사용 예

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);
		}
}

출력 결과 :

1
2
3
23

ConcurrentSkipListSet 결과 = [1, 2, 6, 21]

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

소스 코드 분석

ConcurrentSkipListMap

ConcurrentSkipListMap은 둘 다 순서가 지정된 해시 테이블이지만 TreeMap과 유사합니다. 그러나 먼저 스레드 안전 메커니즘이 다릅니다 .TreeMap은 스레드로부터 안전하지 않은 반면 ConcurrentSkipListMap은 스레드로부터 안전합니다. 둘째, ConcurrentSkipListMap은 테이블을 점프하여 구현하고 TreeMap은 red-black 트리로 구현합니다.

ConcurrentSkipListMap의 데이터 구조는 다음 그림과 같습니다.
ConcurrentSkipListMap의 데이터 구조스킵 테이블은 여러 레벨로 나누어 져 있으며 각 레이어는 데이터의 인덱스로 볼 수 있습니다. 이러한 인덱스의 의미는 스킵 테이블에서 데이터 검색 속도를 높이기위한 것입니다. 각 계층의 데이터가 정렬되고 상위 계층의 데이터는 다음 계층의 데이터의 하위 집합이며 첫 번째 계층 (수준 1)에는 모든 데이터가 포함됩니다. 수준이 높을수록 점프가 커지고 포함 된 데이터가 포함됩니다. 적게. 점프 테이블에는 헤더가 포함되어 있으며 데이터를 검색 할 때 위에서 아래로, 왼쪽에서 오른쪽으로 검색합니다.

먼저 데이터 "7,14,21,32,37,71,85"시퀀스를 예로 들어 점프 테이블을 간략하게 설명합니다. 아래 그림과 같이 점프 테이블에서 "32"노드 경로를 찾습니다.
예

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은 ConcurrentSkipListMap을 통해 구현됩니다. 실제로 ConcurrentNavigableMap ConcurrentSkipListMap의 구현 클래스 인 ConcurrentNavigableMap 객체 m을 포함합니다. ConcurrentSkipListMap의 키만 사용하고 해당 값은 빈 객체입니다.

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>();
    }
}

추천

출처blog.csdn.net/u014134750/article/details/103549166