Java ConcurrentSkipListMap

时间:2020-01-09 10:35:09  来源:igfitidea点击:

这篇文章讨论了java.util.concurrent包中的ConcurrentSkipListMap类以及该类实现的接口ConcurrentNavigableMap。

Java中的ConcurrentSkipListMap

ConcurrentSkipListMap是一个线程安全的可伸缩Map,以排序方式存储其元素。默认情况下,map是根据其键的自然顺序排序的,或者根据在映射创建时提供的Comparator进行排序,具体取决于所使用的构造函数。

Java ConcurrentSkipListMap类实现了SkipList的并发变体,从而为containsKey,get,put和remove操作及其变体提供了预期的平均log(n)时间成本。插入,删除,更新和访问操作可以安全地由多个线程同时执行。在Java 1.6中添加了ConcurrentSkipListMap。

SkipList数据结构

根据https://en.wikipedia.org/wiki/Skip_list –跳过列表是一种数据结构,可以在有序元素序列中进行快速搜索。通过维护子序列的链接层次结构,使快速搜索成为可能,每个连续的子序列跳过的元素比上一个要少。

如我们所见,为了更快地进行搜索,跳过列表要求元素按有序顺序排列,这就是为什么在Java ConcurrentSkipListMap中对元素进行排序的原因。

Java中的ConcurrentNavigableMap

Java中的ConcurrentSkipListMap实现了ConcurrentNavigableMap接口,其中ConcurrentNavigableMap依次扩展了ConcurrentMap和NavigableMap接口。

  • ConcurrentMap –一个提供线程安全性和原子性保证的Map。因此,存在诸如putIfAbsent(),remove()之类的方法,其中的操作是原子执行的。

  • NavigableMap –扩展了SortedMap的导航方法,返回给定搜索目标的最接近匹配项。因此,它具有诸如lowerEntry(K),floorEntry(K),lowerKey(K),floorKey(K),ceilingKey(K)之类的方法,用于将最匹配的键返回给所传递的键。

在Java 1.6中添加了ConcurrentNavigableMap接口。

Java ConcurrentSkipListMap构造函数

  • ConcurrentSkipListMap()–构造一个新的空映射,根据键的自然顺序进行排序。

  • ConcurrentSkipListMap(Comparator <?super K>比较器)–构造一个新的空映射,根据指定的比较器排序。

  • ConcurrentSkipListMap(Map <?扩展K ,?扩展V> m)–构造一个新映射,该映射包含与给定映射相同的映射,并根据键的自然顺序进行排序。

  • ConcurrentSkipListMap(SortedMap <K,?extended V> m)–构造一个新地图,该地图包含与指定排序后的地图相同的地图,并使用相同的顺序。

ConcurrentSkipListMap Java示例

public class SkipMapDemo {
  public static void main(String[] args) {
    // Creating ConcurrentSkipListMap
    ConcurrentNavigableMap<String, String> cityTemperatureMap = new ConcurrentSkipListMap<String, String>();

    // Storing elements
    cityTemperatureMap.put("Delhi", "24");
    cityTemperatureMap.put("Mumbai", "32");
    cityTemperatureMap.put("Chennai", "35");
    cityTemperatureMap.put("Bangalore", "22" );
    cityTemperatureMap.put("Kolkata", "28");

    Set<Map.Entry<String, String>> cityTempSet = cityTemperatureMap.entrySet();
    cityTempSet.forEach((m)->System.out.println("key " + m.getKey() 
				 + " value " + m.getValue()));
  }
}

输出:

key Bangalore value 22
key Chennai value 35
key Delhi value 24
key Kolkata value 28
key Mumbai value 32

如我们所见,这些元素按其键排序,并且使用自然顺序,因为在创建ConcurrentSkipListMap时未传递任何比较器。

ConcurrentSkipListMap不允许为空

Java中的ConcurrentSkipListMap类不允许使用空键或者空值。添加空键或者值会导致抛出NullPointerException。

例如在上面使用的示例代码中,如果我们尝试添加空键,结果将如下所示。

cityTemperatureMap.put(null, "28");

Exception in thread "main" java.lang.NullPointerException
	at java.base/java.util.concurrent.ConcurrentSkipListMap.doPut(ConcurrentSkipListMap.java:597)
	at java.base/java.util.concurrent.ConcurrentSkipListMap.put(ConcurrentSkipListMap.java:1345)

ConcurrentSkipListMap示例中的导航方法

public class SkipMapDemo {

  public static void main(String[] args) {
    // Creating ConcurrentSkipListMap
    ConcurrentNavigableMap<Integer, String> numberMap = new ConcurrentSkipListMap<Integer, String>();

    // Storing elements
    numberMap.put(1, "ONE");
    numberMap.put(2, "TWO");
    numberMap.put(5, "FIVE");
    numberMap.put(8, "EIGHT" );
    numberMap.put(10, "TEN");
    numberMap.put(16, "SIXTEEN");

    System.out.println("** reverse order view of the map **");

    //Returns a reverse order view of the mappings
    ConcurrentNavigableMap<Integer, String> reverseNumberMap = numberMap.descendingMap();

    Set<Map.Entry<Integer, String>> numSet = reverseNumberMap.entrySet();
    numSet.forEach((m)->System.out.println("key " + m.getKey() 
         + " value " + m.getValue()));
    System.out.println("** First entry in the the map **");
    //Returns a key-value mapping associated with the least key in this map
    Map.Entry<Integer, String> mapEntry = numberMap.firstEntry();
    System.out.println("key " + mapEntry.getKey() + " value " + mapEntry.getValue());

    System.out.println("** Floor entry Example **");
    //Returns a key-value mapping associated with the greatest key less than or equal to the given key
    mapEntry = numberMap.floorEntry(7);
    System.out.println("key " + mapEntry.getKey()  + " value " + mapEntry.getValue());

    System.out.println("** Ceiling entry Example **");
    //Returns a key-value mapping associated with the least key greater than or equal to the given key
    mapEntry = numberMap.ceilingEntry(7);
    System.out.println("key " + mapEntry.getKey()  + " value " + mapEntry.getValue());
  }
}

输出:

** reverse order view of the map **
key 16 value SIXTEEN
key 10 value TEN
key 8 value EIGHT
key 5 value FIVE
key 2 value TWO
key 1 value ONE
** First entry in the the map **
key 1 value ONE
** Floor entry Example **
key 5 value FIVE
** Ceiling entry Example **
key 8 value EIGHT