ConcurrentSkipListMap学习笔记

ConcurrentSkipListMap学习笔记JDK 版本 AdoptOpenJDK 0 10 91 基本概念 ConcurrentSk 是在 JDK1 6 中新增的 为了对高并发场景下的有序 Map 提供更好的支持 它有几个特点 高并发场景 key 是有序的添加 删除 查找操作都是基于跳表结构 SkipList 实现的 2 跳表 SkipList 跳表 SkipList 是一种类似于链表的数据结构 其查询 插入 删除的时间复杂度都是 O logn 在传统的单链表结构中 查找某个元素需要从链表的头部按顺序遍历

JDK版本:AdoptOpenJDK 11.0.10+9

1 基本概念

ConcurrentSkipListMap是在JDK 1.6中新增的,为了对高并发场景下的有序Map提供更好的支持,它有几个特点:

  • 高并发场景
  • key是有序的
  • 添加、删除、查找操作都是基于跳表结构(Skip List)实现的
  • keyvalue都不能为null

2 跳表(Skip List)

跳表(Skip List)是一种类似于链表的数据结构,其查询、插入、删除的时间复杂度都是 O(logn)

在传统的单链表结构中,查找某个元素需要从链表的头部按顺序遍历,直到找到目标元素为止,查找的时间复杂度为O(n)

而跳表结合了树和链表的特点,其特性如下:

  1. 跳表由很多层组成;
  2. 每一层都是一个有序的链表;
  3. 最底层的链表包含所有元素;
  4. 对于每一层的任意一个节点,不仅有指向其下一个节点的指针,也有指向其下一层的指针;
  5. 如果一个元素出现在Level n层的链表中,则它在Level n层以下的链表也都会出现。

Skip List例子:

下图是一种可能的跳表结构:

在这里插入图片描述

如图,[1][40]节点有3层,[8][18]节点有2层。每一层都是有序的链表。

如果要查找目标节点[15],大致过程如下:

  1. 首先查看[1]节点的第1层,发现[1]节点的下一个节点为[40],大于15,那么查找[1]节点的下一层;
  2. 查找[1]节点的第2层,发现[1]节点的下一个节点为[8],小于15,接着查看下一个节点,发现下一个节点是[18],大于15,因此查找[8]节点的下一层;
  3. 查找[8]节点的第2层,发现[8]节点的下一个节点是[10],小于15,接着查看下一个节点[13],小于15,接着查看下一个节点[15],发现其值等于15,因此找到了目标节点,结束查询。

跳表实际上是一种 空间换时间 的数据结构。

3 ConcurrentSkipListMap结构

ConcurrentSkipListMap用到了两种结构的节点。

Node节点代表了真正存储数据的节点,包含了keyvalue、指向下一个节点的指针next

 static final class Node<K,V> { 
    final K key; // 键 V val; // 值 Node<K,V> next; // 指向下一个节点的指针 Node(K key, V value, Node<K,V> next) { 
    this.key = key; this.val = value; this.next = next; } } 

Index节点代表了跳表的层级,包含了当前节点node、下一层down、当前层的下一个节点right

 static final class Index<K,V> { 
    final Node<K,V> node; // 当前节点 final Index<K,V> down; // 下一层 Index<K,V> right; // 当前层的下一个节点 Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) { 
    this.node = node; this.down = down; this.right = right; } } 

如图所示,Node节点将真实的数据按顺序链接起来,Index节点组成了跳表中多层次的索引结构。

在这里插入图片描述

4 使用案例

下面是对ConcurrentSkipListMap的简单使用的一个例子:

public class ConcurrentSkipListMapTest { 
    public static void main(String[] args) { 
    ConcurrentSkipListMap<Integer, String> map = new ConcurrentSkipListMap<>(); map.put(4, "4"); map.put(5, "5"); map.put(1, "1"); map.put(6, "6"); map.put(2, "2"); System.out.println(map.keySet()); System.out.println(map); System.out.println(map.descendingKeySet()); System.out.println(map.descendingMap()); } } 

输出为:

[1, 2, 4, 5, 6] {1=1, 2=2, 4=4, 5=5, 6=6} [6, 5, 4, 2, 1] {6=6, 5=5, 4=4, 2=2, 1=1} 

源码解析可以看看这篇文章:Java多线程进阶(二五)—— J.U.C之collections框架:ConcurrentSkipListMap

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/230159.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午3:00
下一篇 2026年3月16日 下午3:00


相关推荐

  • POJ2239 Selecting Courses【二部图最大匹配】

    POJ2239 Selecting Courses【二部图最大匹配】

    2022年1月14日
    59
  • httpclient 4种关闭连接

    httpclient 4种关闭连接Java代码 HttpClient client = new HttpClient();  HttpMethod method = new GetMethod("http://www.apache.org");  try {    client.executeMethod(method);    byte[] responseBody = null;        responseBody = m…

    2022年7月22日
    12
  • java数组初始化的方式_java数组初始化方式

    java数组初始化的方式_java数组初始化方式在使用一个新的数组之前 要先对其中的数值进行设置 也就是我们常说的初始化工作 因为数组有长度和内容的区分 所以常见的两种初始化方法是动态和静态 另外一种就是默认初始化 下面我们对数组的初始化概念进行理解 区分两种初始化方法 然后就三种初始化带来分别的详解 1 概念在内存当中创建一个数组 并且向其中赋予一些默认值 2 常见的初始化方式 1 动态初始化 指定长度 2 静态初始化 指定内容 3 静态初

    2026年3月20日
    2
  • latex中如何正确输入 双引号「建议收藏」

    latex中如何正确输入 双引号「建议收藏」latex中输入双引号时,如果都直接用键盘上的双引号键,打出的是一顺撇的。左面引号的正确输入法是:按两次“Tab上面,数字1左面那个键”。至于后边的引号,与老方法是一样的,即按两次单引号键(或一次SHIFT+单引号键—也就是一次双引号键啦怎么输入左单引号、左双引号、右单引号、有双引号?左单引号:`(键盘上1旁边的那个);左双引号:“;右单引号:'(键盘分号的右边那个);右双引号:”或”。在

    2022年4月19日
    933
  • java开发工程师简历模板,2022最新「建议收藏」

    模块科技招聘官莅临千锋教育招聘Java开发工程师招聘官谢经理莅临千锋教育成都分校招聘10位Java开发工程师,谢经理在面试前的宣讲会上,为学员详细介绍了模块科技的发展现状和岗位需求,让学员对公司和岗位有了充分的认识。在随后的面试环节,学员们基于对企业的了解和自身职业发展规划,纷纷递交上自己的简历。年薪120W的架构师简历你见过吗?java程序员该如何达到?这可以归因于Java是德国对于软件工程师来说,因为它用于为许多行业构建高可伸缩性的应用程序。大多数企业服务依赖于Java来支持企业日常那年薪1

    2022年4月14日
    181
  • 部署OpenClaw,在飞书拥有7×24小时专属AI助手

    部署OpenClaw,在飞书拥有7×24小时专属AI助手

    2026年3月15日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号