当前位置: 首页 > news >正文

Java集合框架:Set、List、Map及HashMap底层实现详解

一、集合框架概述

Java集合框架主要分为三大类接口:

  1. ​List(列表)​​:有序集合,元素可重复

    • 实现类:ArrayList、LinkedList、Vector

  2. ​Set(集)​​:无序集合,元素不可重复

    • 实现类:HashSet、LinkedHashSet、TreeSet

  3. ​Map(映射)​​:键值对集合

    • 实现类:HashMap、LinkedHashMap、TreeMap、Hashtable

二、List接口及实现类

1. ArrayList

  • ​底层结构​​:动态数组

  • ​特点​​:

    • 随机访问快(O(1))

    • 插入删除慢(需要移动元素,O(n))

    • 线程不安全

  • ​扩容机制​​:

    • 默认初始容量:10

    • 扩容公式:newCapacity = oldCapacity + (oldCapacity >> 1)(即1.5倍)

    • 扩容时调用Arrays.copyOf()复制数组

2. LinkedList

  • ​底层结构​​:双向链表

  • ​特点​​:

    • 插入删除快(O(1))

    • 随机访问慢(需要遍历,O(n))

    • 实现了Deque接口,可用作队列或栈

3. Vector

  • ​底层结构​​:动态数组(类似ArrayList)

  • ​特点​​:

    • 线程安全(方法使用synchronized修饰)

    • 性能较差

    • 扩容默认增长为原来的2倍

三、Set接口及实现类

1. HashSet

  • ​底层结构​​:基于HashMap实现

  • ​特点​​:

    • 元素无序

    • 允许null值

    • 线程不安全

  • ​实现原理​​:

    // 实际使用HashMap存储
    private transient HashMap<E,Object> map;
    // 值使用固定Object对象
    private static final Object PRESENT = new Object();public boolean add(E e) {return map.put(e, PRESENT)==null;
    }

2. LinkedHashSet

  • ​底层结构​​:继承HashSet,基于LinkedHashMap实现

  • ​特点​​:

    • 维护插入顺序

    • 性能略低于HashSet

3. TreeSet

  • ​底层结构​​:基于TreeMap实现(红黑树)

  • ​特点​​:

    • 元素有序(自然顺序或Comparator)

    • 不允许null值

    • 基本操作时间复杂度O(log n)

四、Map接口及实现类

1. HashMap(重点详解)

底层结构(JDK8及以后)
  • ​数组+链表+红黑树​​的混合结构

    • 默认初始容量:16

    • 负载因子(loadFactor):0.75(默认)

    • 链表转红黑树阈值:8

    • 红黑树转链表阈值:6

核心字段
transient Node<K,V>[] table; // 哈希桶数组
transient int size;          // 键值对数量
int threshold;               // 扩容阈值(capacity * loadFactor)
final float loadFactor;      // 负载因子
存储结构图示
数组索引: 
0 -> null
1 -> Node<K,V> -> Node<K,V> -> null
2 -> TreeNode<K,V> -> TreeNode<K,V> -> null
...
15 -> null
扩容机制
  1. ​触发条件​​:

    • 当前size >= threshold

    • 链表长度 >= TREEIFY_THRESHOLD(8)但数组长度 < MIN_TREEIFY_CAPACITY(64)

  2. ​扩容过程​​:

    • 创建新数组(大小为原数组2倍)

    • 重新计算所有元素的哈希位置(非常耗时的操作)

    • JDK8优化:元素在新数组中的位置要么不变,要么是原位置+原数组长度

  3. ​源码分析​​(简化版):

final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;// 1. 计算新容量和阈值if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // 双倍}// ... 其他初始化情况处理// 2. 创建新数组Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;// 3. 迁移数据if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else {// 链表优化重hashNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}
哈希冲突解决
  1. ​链表法​​:哈希冲突时,在数组对应位置形成链表

  2. ​红黑树优化​​:当链表长度>=8且数组长度>=64时,链表转为红黑树

重要特性
  • 允许null键和null值

  • 线程不安全

  • 迭代时使用fail-fast机制

2. LinkedHashMap

  • ​底层结构​​:继承HashMap,增加双向链表维护插入顺序

  • ​特点​​:

    • 可预测的迭代顺序

    • 性能略低于HashMap

3. TreeMap

  • ​底层结构​​:红黑树

  • ​特点​​:

    • 按键的自然顺序或Comparator排序

    • 基本操作时间复杂度O(log n)

4. Hashtable

  • ​底层结构​​:类似JDK7的HashMap(数组+链表)

  • ​特点​​:

    • 线程安全(方法使用synchronized修饰)

    • 不允许null键和null值

    • 初始容量11,扩容为2n+1

五、HashMap常见问题

1. 为什么链表长度超过8转为红黑树?

根据泊松分布,哈希冲突达到8的概率极低(小于千万分之一),此时使用红黑树(查询时间O(log n))比链表(O(n))更高效。

2. 为什么初始容量是16?

  • 2的幂次方有利于通过位运算计算索引:index = hash & (length-1)

  • 16是经验值,在大多数情况下提供了良好的性能平衡

3. 为什么负载因子是0.75?

时间和空间的折中:

  • 过高(如1.0):减少空间开销,但增加查询成本

  • 过低(如0.5):减少哈希冲突,但增加扩容频率

4. 如何设计好的键对象?

  • 重写hashCode()和equals()方法

  • 保证不可变性(否则可能导致哈希值变化)

  • 实现Comparable接口(如需排序)

六、总结对比

集合类

底层结构

线程安全

有序性

允许null

ArrayList

动态数组

插入顺序

LinkedList

双向链表

插入顺序

Vector

动态数组

插入顺序

HashSet

HashMap

LinkedHashSet

LinkedHashMap

插入顺序

TreeSet

红黑树

自然顺序

HashMap

数组+链表+红黑树

LinkedHashMap

HashMap+双向链表

插入/访问顺序

TreeMap

红黑树

自然顺序

Hashtable

数组+链表

理解这些集合类的底层实现和特性,有助于在实际开发中选择最合适的数据结构,编写出更高效的Java代码。

http://www.dtcms.com/a/341242.html

相关文章:

  • 在Excel和WPS表格中制作可打印的九九乘法表
  • Flask高效数据库操作指南
  • Qt音乐播放器项目实践:本地持久化与边角问题处理
  • 对象存储 COS 端到端质量系列——终端 COS SDK 网络优化
  • 开发中使用——鸿蒙特有的图片保存到相册的方式
  • 气泡水位计在水库大坝安全监测中的应用
  • 大语言模型研究进展
  • 【MAR】OSCNet+学习笔记
  • 机器学习总复习
  • Redis知识总结
  • IDEA:控制台中文乱码
  • 【JavaEE】多线程 -- 定时器
  • 无人机感知系统详解
  • Excel表格复制到word中格式错乱
  • 【Java】深入浅出Spring中的@Autowired:自动注入的奥秘
  • 机器翻译 (Machine Translation) 经典面试笔试50题(包括详细答案)
  • imx6ull-驱动开发篇29——Linux阻塞IO 实验
  • Java并发容器详解
  • 【LLIE专题】BEM:一对多映射,贝叶斯增强模型
  • Deepseek+python自动生成禅道测试用例
  • 将集合拆分成若干个batch,并将batch存于新的集合
  • WMS仓储管理系统如何解决仓库的呆滞库存
  • 鸿蒙安卓前端中加载丢帧:ArkWeb分析
  • 第5.7节:awk赋值运算
  • IPSEC安全基础
  • Qt 中最经典、最常用的多线程通信场景
  • TDengine IDMP 运维指南(数据导入导出)
  • WIN10/WIN11:无法下载所有必需的文件 0x80072EE2 0x20000(未解决)
  • C++ std::sort的应用总结
  • Unity 大量子弹和小怪碰撞检测优化