中间件核心技术与实战
丁威
中通快递资深架构师,RocketMQ 社区首席布道师
19674 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
中间件核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

03 | 数组与链表:存储设计的基石有哪些?

你好,我是丁威。
从这节课开始,我们就要进行基础篇的学习了。想要熟练使用中间件解决各种各样的问题,首先需要掌握中间件的基础知识。
我认为,中间件主要包括如下三方面的基础:数据结构、JUC 和 Netty,接下来的两节课,我们先讲数据结构。
数据结构主要解决的是数据的存储方式问题,是程序设计的基座。
按照重要性和复杂程度,我选取了数组和链表、键值对 (HashMap)、红黑树、LinkedHashMap 和 PriorityQueue 几种数据结构重点解析。其中,数组与链表是最底层的两种结构,是后续所有数据结构的基础。
我会带你分析每种结构的存储结构、新增元素和搜索元素的方式、扩容机制等,让你迅速抓住数据结构底层的特性。当然,我还会结合一些工业级实践,带你深入理解这些容器背后蕴含的设计理念。
说明一下,数据结构其实并不区分语言,但为了方便阐述,这节课我主要基于 Java 语言进行讲解。

数组

我们先来看下数组。
数组是用于储存多个相同类型数据的集合,它具有顺序性,并且也要求内存空间必须连续。高级编程语言基本都会提供数组的实现。
为了更直观地了解数组的内存布局,我们假设从操作系统申请了 128 字节的内存空间,它的数据结构可以参考下面这张图:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了数组、ArrayList、LinkedList和HashMap这几种数据结构在程序设计中的重要性和应用。首先介绍了数组的内存连续性和高效的随机访问性能,并以Java语言为例,解释了数组的内存布局和访问机制,以及在中间件领域的应用。其次,对ArrayList进行了分析,包括底层存储结构、扩容机制和数据访问特性。最后,对LinkedList的底层存储结构和操作进行了介绍,并与ArrayList进行了对比,突出了链表在头尾添加/删除节点的高效性。文章还深入探讨了HashMap的底层存储结构和扩容机制,以及在JDK8中引入的红黑树优化策略。总体而言,本文通过对数据结构特性和应用进行深入剖析,为读者提供了对这些存储设计的基础知识和工业级实践的深入理解。文章内容涵盖了数据结构的基本原理和实际应用,对程序设计人员和计算机科学学习者具有重要参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《中间件核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(7)

  • 最新
  • 精选
  • 苜蓿°
    置顶
    问题1 网上找寻资料学习了一下: (1) 开放定址法 ①线性探测法 实现过程:当我们的所需要存放值的位置被占了,我们就往后面一直加1并对m取模直到存在一个空余的地址供我们存放值,取模是为了保证找到的位置在0~m-1的有效空间之中。 公式:h(x)=(Hash(x)+i)mod (Hashtable.length);(i会逐渐递增加1) 存在问题:出现非同义词冲突(两个不相同的哈希值,抢占同一个后续的哈希地址)被称为堆积(聚集)现象 ②平方探测法(二次探测) 实现过程: 当我们的所需要存放值的位置被占了,会前后寻找而不是单独方向的寻找 公式:h(x)=(Hash(x) +i)mod (Hashtable.length);(i依次为+(i^2)和-(i^2)) 存在问题:相比线性探测减少找寻时间,但不会减少堆积(聚集)现象 (2)再哈希法 实现过程:同时构造多个不同的哈希函数,等发生哈希冲突时就使用第二个、第三个……等其他的哈希函数计算地址,直到不发生冲突为止 存在问题:不易发生聚集,但是增加了计算时间 (3)建立公共溢出区 实现过程:将哈希表分为基本表和溢出表,将发生冲突的都存放在溢出表中 存在问题:需要实时动态维护两张哈希表

    作者回复: 感谢你的分享,这个必须得🔝

    2022-06-29
    13
  • 末日,成欢
    HashMap 中哈希槽的容量为什么必须为 2 的倍数? 当我们计算key的索引下标时, 通过会对key的哈希码对数组取模运算来计算出下标值。 而JAVA中的大神对取模运算又进行了一次优化。 将取模运算优化为与运算。 为什么能被优化为与运算呢? 举个简单的例子: hashcode为11,数组容量为4。 我们知道对于2的n次方一定能整除2的n-1次方。 11对8取模,获取它的余数,也就是它的下标值。 转化为二进制1011 % 0100, 也就是1011/0100, 因为2的n次方一定能整除2的n-1次方的缘故,高位直接舍弃。 其余的无法被整数的也就是它的余数,它的低位就是它的余数, 获取它的低位就可以通过x011&(0100-1)=>也就是hash & (n-1),能推导出这个公式是建立在数组的容量是2的倍数的前提上。 为什么要优化为与运算呢? 我的猜想,计算机对于与运算的指令比取模运算的指令要少的多, 故与运算要高效的多。 最后,我还想再说一个key获取哈希值的优化。 我们知道,生成哈希值的原则就是要尽量让所有的元素都参与到计算。 key获取哈希值, 并不是简单的直接获取哈希码, 而是该key的哈希值高低16位再一次又进行了异或运算,这样就可以让哈希值再次变化。 其实也可以不变化的,但是对于起始容量比较小,大量的元素如果低位都相同,就可能会造成大量的哈希冲突。 而再一次进行异或运算, 即使相同的低位也可以再次变化, 减小hash冲突。

    作者回复: 超级👍,讲真,是的HashMap中那个hash算法,为什么能让值更散列,其实我还没理解,也希望你和我关于这个问题进行一步沟通,🙏

    2022-06-21归属地:上海
    9
  • @%初%@
    hashmap,容量设计成2的整数幂,我理解有两点: 1.就是设计成2整数幂可以利用位运算的高效,快速找到下标。 2.为了扩容搬运元素方便,小于之前size的元素,可以直接搬运到新size的原始位置,而超过原始size的元素,只需要下标加上原始size即可,实战了高效的扩容元素迁移。

    作者回复: 理解的非常到位。

    2022-07-01
    2
    3
  • 独自等待
    RocketMQ这块的分析还可以

    作者回复: 谢谢认可,多多交流

    2022-06-17
    3
  • 夜空中最亮的星
    分析到位 图标很棒
    2022-07-11
  • William Ning
    https://time.geekbang.org/column/article/64586 数据结构与算法之美 对照着学习~~
    2022-07-06
  • 门窗小二
    hashmap1.8之前采用头插法的解释很牵强啊!通过复杂度比较的话头尾不都是O(1)
    2022-06-26
    3
收起评论
显示
设置
留言
7
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部