集合复习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. 判断table[i] == null,条件成立则新建节点添加
  4. 条件不成立则
    • 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value
    • 判断table[i]是否为treeNode,即table[i]是否为红黑树,则直接在树中插入键值对
    • 遍历table[i],链表尾部插入数据,然后判断长度是否大于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
相关推荐
国思RDIF框架9 小时前
RDIFramework.NET CS 敏捷开发框架 V6.3 版本重磅发布!.NET8+Framework双引擎,性能升级全维度进化
后端·.net
心在飞扬9 小时前
ReRank重排序提升RAG系统效果
前端·后端
喝茶与编码9 小时前
Python异步并发控制:asyncio.gather 与 Semaphore 协同设计解析
后端·python
不早睡不改名9 小时前
网络编程基础:从BIO到NIO再到AIO(一)
后端
开源之眼9 小时前
《github star 加星 Taimili.com 艾米莉 》为什么Java里面,Service 层不直接返回 Result 对象?
java·后端·github
心在飞扬9 小时前
RAPTOR 递归文档树优化策略
前端·后端
zone77399 小时前
003:RAG 入门-LangChain 读取图片数据
后端·python·面试
心在飞扬9 小时前
LangChain Parent Document Retriever (父文档检索器)
后端
zone77399 小时前
002:RAG 入门-LangChain 读取文本
后端·算法·面试
用户8356290780519 小时前
在 PowerPoint 中用 Python 添加和定制形状的完整教程
后端·python