14、Java 基础硬核复习:数据结构与集合源码的核心逻辑与面试考点
一、核心知识体系:数据结构的"两大维度"与"集合源码剖析"
本章的知识围绕"数据结构基础"和"集合底层实现"展开,可归纳为两大维度 (数据结构分类、集合源码)和三大专题(数据结构概述、核心数据结构、集合源码)。
1. 数据结构基础:存储与运算的核心逻辑
数据结构是算法的基石,主要分为存储结构 和运算结构:
- 存储结构 :
- 顺序存储(如数组):内存地址连续,查询快(O(1)),增删慢(需移动元素,O(n));
- 链式存储(如链表):内存地址不连续,增删快(只需修改指针,O(1)),查询慢(需遍历,O(n))。
- 运算结构 :
- 栈(Stack):后进先出(LIFO),如方法调用栈(递归)、表达式求值(后缀表达式);
- 队列(Queue) :先进先出(FIFO),如线程池任务队列(
BlockingQueue)、消息队列(Kafka); - 二叉树(Binary Tree):每个节点最多两个子节点,遍历方式包括前序(根-左-右)、中序(左-根-右)、后序(左-右-根)(中序遍历二叉搜索树可得到有序序列)。
2. 集合源码剖析:ArrayList、LinkedList、HashMap的底层实现
集合框架的底层是数据结构的具体应用,需掌握其核心实现:
- ArrayList :
- JDK7:像"饿汉式",创建对象时直接初始化长度为10的数组;
- JDK8 :像"懒汉式",创建对象时数组为空,第一次调用
add时才初始化长度为10; - 扩容机制 :默认扩容为原来的1.5倍 (
newCapacity = oldCapacity + (oldCapacity >> 1)),使用Arrays.copyOf复制老数组(比JDK7的System.arraycopy更高效)。
- LinkedList :
- 基于双向链表,每个节点包含
prev(前驱)、item(数据)、next(后继)指针,增删操作时间复杂度O(1),查询慢(O(n))。
- 基于双向链表,每个节点包含
- HashMap :
- JDK7 :数组+链表,哈希冲突用头插法(极端情况下链表成环,导致死循环);
- JDK8 :数组+链表+红黑树 ,哈希冲突用尾插法(解决死循环),当链表长度>8且数组长度>64时,链表转为红黑树(查询复杂度从O(n)降到O(log n));
- put流程 :计算
key的哈希值→定位数组索引→若索引为空,直接插入;若不为空,判断是否为key相同(equals),相同则覆盖;不同则解决哈希冲突(链表/红黑树)。
- LinkedHashMap :
- 继承自
HashMap,在Entry中增加before和after指针,形成双向链表维护插入顺序(用于LRU缓存)。
- 继承自
- HashSet :
- 内部用
HashMap实现,元素作为HashMap的key,value固定为PRESENT(Object对象),去重依赖HashMap的key唯一性。
- 内部用
二、高频面试考点:必考"源码死穴",掌握这些=掌握数据结构与集合核心
本章的面试题全是"造火箭"级别的,需死记硬背关键数值和流程:
1. ArrayList的扩容机制(必考 Top1)
- 考点:JDK7和JDK8的扩容区别?
- 答案 :
- JDK7 :默认容量10,每次扩容1.5倍,扩容时用
System.arraycopy复制数组; - JDK8 :默认容量10(懒加载),扩容1.5倍,用
Arrays.copyOf(更高效)。
- JDK7 :默认容量10,每次扩容1.5倍,扩容时用
2. HashMap的底层结构(大厂必问)
- 考点:JDK7和JDK8的区别?为什么引入红黑树?
- 答案 :
- JDK7 :数组+链表,极端情况下(所有
key哈希冲突),查找复杂度退化为O(n); - JDK8:数组+链表+红黑树,链表长度>8且数组长度>64时转红黑树,查询复杂度优化为O(log n)。
- JDK7 :数组+链表,极端情况下(所有
3. HashMap的扩容(Resize)
- 考点:什么时候扩容?为什么是2的幂次方?
- 答案 :
- 时机 :元素个数>
容量×负载因子(16×0.75=12); - 扩容倍数:2倍;
- 2的幂次方原因 :让
hash % n等价于hash & (n-1)(位运算更高效)。
- 时机 :元素个数>
4. HashMap线程不安全
- 考点:多线程下会发生什么?
- 答案 :
- JDK7:并发扩容时链表成环(死循环);
- JDK8 :解决死循环,但仍会数据覆盖 (多线程请用
ConcurrentHashMap)。
5. HashSet的底层
- 考点:HashSet怎么存数据?
- 答案 :
HashSet底层是HashMap,把数据作为key存进去,value是固定的PRESENT(Object对象)。
三、学习建议:从"理论"到"实践"的跃迁
- 看源码 :打开IDE(如IntelliJ),按住
Ctrl点进java.util.HashMap,看put方法、扩容逻辑; - 记关键数字 :记住
10(List初始容量)、16(Map初始容量)、0.75(负载因子)、8(转红黑树阈值)、64(数组长度阈值); - 理解结构 :脑子里要有"双向链表"的图(如
LinkedList、LinkedHashMap的节点结构),理解增删的指针操作。