详解:HashMap与TreeMap、HashTable的区别

60 阅读3分钟

本文将从多个维度对比三者的核心差异,帮助开发者根据场景选择合适的数据结构:

1、 线程安全性

  • HashTable:通过 synchronized 修饰所有方法实现线程安全,但高并发下性能差。
  • HashMap/TreeMap:非线程安全,多线程需手动同步或使用并发容器。
特性HashMapTreeMapHashTable
线程安全非线程安全非线程安全线程安全(方法全同步)
替代方案使用 ConcurrentHashMap使用 ConcurrentSkipListMap已过时,推荐 ConcurrentHashMap

2、内部实现与数据结构

  • HashMap:哈希表动态扩容,链表长度超阈值(默认8)转为红黑树。
  • TreeMap:基于红黑树维护键的有序性,支持范围查询(如 subMap())。
  • HashTable:传统哈希表,无红黑树优化,冲突处理仅用链表。
特性HashMapTreeMapHashTable
数据结构数组 + 链表/红黑树(JDK8+)红黑树数组 + 链表
冲突解决拉链法 + 树化优化(JDK8+)自然无冲突(树结构)拉链法(无树化优化)
有序性无序按键自然/比较器排序无序

3、性能对比

  • HashMap:哈希冲突少时性能最佳,冲突多时退化为链表或树。
  • TreeMap:所有操作稳定在对数时间,适合需要排序的场景。
  • HashTable:同步锁导致并发性能差,单线程下也不如 HashMap。
操作HashMap(平均)TreeMapHashTable
插入/删除O(1)O(log n)O(1)
查询O(1)O(log n)O(1)
遍历O(n)O(n)(有序)O(n)

4、Null 键值支持

  • TreeMap:插入 null 键会抛出 NullPointerException(依赖自然排序或比较器)。
  • HashTable:插入 null 键或值直接抛出异常。
特性HashMapTreeMapHashTable
Null 键✅ 允许一个 null 键❌ 不允许(需比较)❌ 不允许
Null 值✅ 允许多个 null 值✅ 允许(视比较器而定)❌ 不允许

5、 扩容机制

  • HashMap:扩容时重新哈希分布键值,优化后减少计算(JDK8+)。
  • HashTable:扩容策略较为保守,性能开销更大。
特性HashMapHashTableTreeMap
初始容量1611无固定容量
扩容规则2 倍增长(容量×2)2 倍+1(旧容量×2 +1)动态调整树结构
负载因子0.75(默认)0.75(默认)不适用

6、使用场景建议

  1. HashMap

    • 快速键值查找,无需排序。
    • 单线程或已手动处理同步的多线程环境。
    • 示例:缓存数据、频率统计。
  2. TreeMap

    • 需要按键排序或范围查询。
    • 示例:有序事件日志、按范围检索数据(如价格区间)。
  3. HashTable

    • 遗留代码兼容,现代开发优先使用 ConcurrentHashMap

7、总结对比表

维度HashMapTreeMapHashTable
线程安全✅(同步方法)
数据结构哈希表+链表/红黑树红黑树哈希表+链表
有序性✅(按键排序)
Null 支持✅键/值❌键,✅值(视情况)❌键/值
性能O(1)(平均)O(log n)O(1)(但同步开销大)
适用场景高频查询、无需排序排序需求、范围查询遗留系统,线程安全需求

通过合理选择数据结构,可显著提升程序性能与代码可维护性。现代开发中,优先考虑 ConcurrentHashMap(线程安全)和 TreeMap(排序需求),避免使用过时的 HashTable

更多分享

  1. 一文带你吃透Android中常见的高效数据结构
  2. 详解:ArrayMap和SparseArray在HashMap上面的改进
  3. 详解:Set集合是如何保证元素不重复的
  4. 详解:LinkedHashMap的工作原理和实现
  5. 一文带你搞懂HashSet和TreeSet的区别