集合复习doge

ArrayList底层的实现原理是什么

  • ArrayList底层是用动态的数组实现的
  • ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10
  • ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组
  • ArrayList在添加数据的时候
    确保数组已使用长度(size)加1后足够存下下一个数据
    计算数组的容量,如果当前数组已使用长度+1后的当前数组长度,则调用grow方法扩容
    确保新增的数据有地方存储之后,则将新元素添加到位于size的位置
    返回添加成功布尔值

ArrayList和LinkedList的区别

  1. 底层数据结构不同,一个是动态数组,一个是双向链表
  2. 性能上的差异:
    • ArrayList 随机访问是 O(1),因为数组支持下标直接定位,arr[5] 底层就是首地址加偏移量,一步到位。但中间插入删除是 O(n),要把后面的元素整体挪动。
    • LinkedList 随机访问是 O(n),得从头或从尾一个个遍历过去。但在头尾插入删除是 O(1),改两个指针就完事。
      实际开发中,90% 以上的场景都该用 ArrayList,因为现代 CPU 对连续内存的缓存命中率远高于链表这种跳来跳去的结构。
  3. 内存占用,前者内存数据连续节省内存,而后者还要加上两个指针,更占内存
  4. 两者都不安全,可以是
提问:你说 ArrayList 中间插入不一定比 LinkedList 慢,能展开讲讲吗?

回答:主要原因是 CPU 缓存的影响。ArrayList 的数据在内存里是连续存放的,访问时缓存命中率高,搬运数据用的 System.arraycopy 也是高度优化的原生方法。LinkedList 虽然插入本身是 O(1),但首先得定位到插入位置,这个遍历过程是 O(n),而且每访问一个节点就可能触发一次缓存缺失。实测在中间位置插入大量数据,ArrayList 往往比 LinkedList 更快。只有在头部频繁插入删除时,LinkedList 才有优势。

HashMap

说一下HashMap的实现原理
  • 底层使用哈希表数据结构,即数组+链表+红黑表(1.8引入,链表长度大于8且数组长度大于64则会从链表转化为红黑树)
  • 添加数据时,先算 Key 的 hashCode,然后用 (table.length-1) & hash算出下标位置 算出的下标位置冲突就用链表串起来,如果同一个下标的链表超过8个节点,则会转化为红黑树
HashMap的put方法的具体流程
  1. 判断键值对数组的table是否为空或为null,否则执行resize方法进行扩容(初始化)
  2. 根据键值对key计算hash值得到数组索引
  3. 判断tablei == null,条件成立则新建节点添加
  4. 条件不成立则
    • 判断tablei的首个元素是否和key一样,如果相同直接覆盖value
    • 判断tablei是否为treeNode,即tablei是否为红黑树,则直接在树中插入键值对
    • 遍历tablei,链表尾部插入数据,然后判断长度是否大于8,大于8则转换为红黑树
  5. 插入成功后判断键值对数量是否超过了最大容量threshold(数组长度 * 0.75)如果超过就要扩容
讲一讲HashMap的扩容机制
  • 在添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次扩容都是达到了扩容阈值(数组长度 * 0.75)
  • 每次扩容的时候都是扩容之前容量的2倍
  • 扩容之后会新创建一个数组需要把老数组中的数据挪到新的数组中
    • 没有hash冲突的的的节点,则使用e.hash & (newCap-1)计算新数组的索引位置
    • 如果是红黑树,走红黑树的添加
    • 如果是链表则需要遍历链表,可能需要拆分链表,判断e.hash & oldCap 是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小的位置
HashMap的寻址算法
  • 计算对象的hashCode(),再进行调用hash()方法进行二次哈希,hashCode值右移16位再异或运算,让哈希分布更均匀
  • 最后(capacity-1)& hash 得到索引
为何HashMap的数组长度一定是2的次幂
  1. 计算索引的时效率更高:如果是2的n次幂可以使用位与运算代替取模
  2. 扩容时重新计算索引效率更高:hash & oldCap == 0的元素留在原地,
    否则新位置=旧位置+oldCap
相关推荐
掘金者阿豪11 小时前
被一个标量子查询折腾了两天,最后发现是数据库自己“偷了懒”
后端
NE_STOP11 小时前
Docker--容器常用命令
java
摇滚侠11 小时前
MSYS2 Builds Hashes Cygwin Builds Hashes 区别
java
武子康11 小时前
Java-08 深入浅出 Mybatis 数据库多对多关系设计:中间表、映射与性能优化
java·后端·spring
明月_清风12 小时前
二进制序列化入门——为什么二进制比文本更快、更小?
后端·protobuf·messagepack
无极低码12 小时前
wsdl转client使用wsimport,高版本openjdk不支持使用 JAX-WS
java
明夜之约12 小时前
Spring Cloud Gateway 深度解析:从路由原理到生产级网关实战
java·spring·spring cloud·gateway
咕白m62512 小时前
Excel 工作表名称读取(Python 实现)
后端·python
Simon5231412 小时前
Spring Bean----5.27学习小记
java·学习·spring
雪隐12 小时前
AI股票小助手00-导言
人工智能·后端