集合复习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
相关推荐
青云计划11 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿11 小时前
Jsoniter(java版本)使用介绍
java·开发语言
Victor35612 小时前
MongoDB(9)什么是MongoDB的副本集(Replica Set)?
后端
Victor35612 小时前
MongoDB(8)什么是聚合(Aggregation)?
后端
探路者继续奋斗12 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
消失的旧时光-194313 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
yeyeye11113 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
A懿轩A13 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
Tony Bai14 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php
乐观勇敢坚强的老彭14 小时前
c++寒假营day03
java·开发语言·c++