该文章不适合初学数据给链表的人看,因为里面涉及到了其他的数据结构和高级的链表数据结构
ArrayList
arrayList的添加流程:有兴趣可以自己debug进去看看
在使用idea进行debug的时候,记得将这个配置关闭,否则不能看到数组中为null的元素
在删除数组元素的时候,是将这个位置的后面的元素全部向前一位
System.arraycopy(
elementData, :从 哪个数组开始移动
index+1, :从哪个位置开始移动
elementData, :移动到哪个数组
index, :移动到数组的哪个位置
numMoved :要移动的长度
);
LinkedList
linkedList的结构就比较简单,每次只是需要改变链表中每个node结点的指针即可
关于栈和队列
栈
栈的结构是后进先出的,所以这种结构使用链表来设计是比较高效的,因为每次都是修改列表的队尾部分,所以使用链表增删改查都效率都是较高的,但是在jdk1.8的时候栈的底层是ArrayList的数据结构,下图的debug的结果:
队列
其实linkedList就是一个队列,它具有队列和双向队列的结果,下图是它的继承图
CopyOnWriteArrayList
写时复制技术
Copy On Write:在写的时候,复制一份副本,保证写和读可以同时发送
示例图
图片来源:CopyOnWriteArrayList实现原理及源码分析
docker中的写时复制
这个图可能有点错误,具体的可以参考
即可读层不懂,只编写可写层
Skip list(跳表)
java中没有跳表的直接实现类,但是在redis中有,在redis有一个操作类型:Sorted Set。Sorted Set的结构如下<key,key对应的值>,通常可以用于实时热点的排行榜。
排行榜的结构如下:<事件名称,事件的热度>,而排行榜是根据热度进行排名的,且热度是可以更新的。所以需要一种数据结构来将更新后的数据放在适当的位置
。
既然是热度排行榜,那么之前的热度肯定是有序的,在有序的结果集中找到适合一新个数值的最快算法是什么?肯定是二分查找啊。
但是遗憾的是,二分查找只适用于数组,不能用于链表。那么有没有一种结构可以作为链表的二分查找
呢?跳表就可以大致做到链表的二分查找效果
下图是跳表的结构:
图片来自于:Redis(十二)-Redis的数据结构之跳表
如果层数足够多的话(以61层为例),大致可以存2^61跳数据,算下来和二分查找的时间复杂度差不多
ziplist(压缩列表)
压缩链表这种数据结构不是java中的,而且java也不可能实现,它是redis对于内存的机制压缩而产生的一种数据结构。
在c语言中,一个指针占用的内存大小是8字节(64bit),而8字节可用表示的内存大小为4294967296
GB,而redis使用的内存很显然是用不了这么多的,那么有没有什么方法替代指针呢?很显然–没有,因为指针可用指向不连续的存储空间。
但是对于连续的存储空间来说,我们可用使用内存空间的占用长度来替代指针,使用4字节(32-1)位的长度来 表示连续的内存空间占用情况,可用表示32G
,而这符合当前机器的标准,所以redis中使用连续的存储空间中的压缩列表
来替代指针,尽可能的压缩机器内存,提高内存利用率
压缩列表结构如下:
参考:一套打通Redis(2)–列表的底层实现-链表、压缩链表与快速列表
可以看出来,压缩列表不适合里面元素太大的场景
quicklist(快表)
顾名思义,即它比一般的链表要快,有点Skip list(跳表的思想),其实例如下:
参考:Redis|快速表、压缩表和双向链表(重点介绍quicklist)
它的结构描述如下:
将压缩列表存储在每一个快表的节点中,当需要某一个位置的数据的适合,可用从压缩列表中得到节点数,如果该数据不知压缩列表的数种,那么就会到下一个快表节点种查找,从而节约了多次在压缩列表中的查询
说明
以上关于redis的各种列表都是基于redis6的,其中redis6的各种数据的底层数据类型如下:
- string:Int、SDS
- List:ZipList、quicklist
- hash:数据量少zipList,数量多hashTable
- set:IntSet、HashSet
- zSet:数据量少zipList,数量多skipList
可以 看出来 ,在很多地方都用了压缩列表的,说明redis对内存的压缩非常极致
listpack(紧凑列表)
这是在redis5的时候出现的数据类型,在redis7的时候替代了zipList。
zipList有一些缺点:
- 查找复杂度高:使用quickList来解决了
- 连续更新风险
连续更新风险:即在非末尾插入了一个元素,后面的数据因为保留了前一个元素的长度,所以后一个数据中保存长度的位置也要发送更改,如果后一个数据更改后的长度又发送了变化,再后一个元素的保存长度数据又要更改,依次往下
listpack的改进是:不保留前一个数据的长度,而是保留当前数据的长度,如图
参考连接:ziplist、quicklist、listpack源码设计解读
总结
列表是一种基础的数据结构,可能很多人认为只是切换一下指针,其中我也是这样认为的,知道后来学习了redis的底层数据结构,才发现列表可以又很多的样式。
知道的越多,越能发现自己的无知