HashMap不保证顺序,TreeMap按key自然顺序或Comparator排序;前者平均O(1),后者稳定O(log n);两者均线程不安全,内存与序列化结构不同。

HashMap不保证顺序,TreeMap按key自然排序
插入顺序和遍历顺序在HashMap中完全不可预测,哪怕你按"a"、"b"、"c"顺序put,迭代时可能返回"b"、"a"、"c"——这是由其哈希桶+链表/红黑树的存储结构决定的。TreeMap则始终按key的自然顺序(或构造时传入的Comparator)维护红黑树,entrySet()、keySet()、values()遍历结果严格有序。
常见错误:用HashMap存时间序列数据(如日志事件),再依赖for-each循环获取“最早到最晚”的结果,实际得不到顺序保证;换成TreeMap又没意识到它要求key必须可比较(Comparable或提供Comparator),导致ClassCastException。
- 如果key是
String、Integer等天然可比较类型,TreeMap开箱即用 - 若key是自定义类,必须实现
Comparable接口,或在构造TreeMap时显式传入Comparator - HashMap允许
null作为key(仅一个)和value;TreeMap只允许null作为value,null作key会抛NullPointerException
HashMap平均O(1)查插删,TreeMap稳定O(log n)
HashMap依赖哈希函数将key映射到数组索引,理想情况下定位桶+遍历链表/树深度极小,所以平均时间复杂度是O(1)。但极端情况(如所有key哈希值相同)会退化为O(n);Java 8后引入红黑树优化,当单个桶内节点≥8且table长度≥64时转为树,最坏变O(log n)。
TreeMap底层是红黑树,所有操作(get、put、remove)都稳定在O(log n),无哈希碰撞问题,也不受key分布影响。
立即学习“Java免费学习笔记(深入)”;
- 高频随机读写、不关心顺序 → 优先HashMap
- 需要范围查询(如
subMap("2023-01", "2023-12"))、前驱后继(floorKey/ceilingKey)→ 只能选TreeMap - 对性能极度敏感且key哈希分布差(如大量短字符串前缀相同),HashMap可能比TreeMap还慢
HashMap线程不安全,TreeMap同样不安全
两者都不是线程安全的集合。多线程并发put可能导致HashMap扩容时死循环(JDK 7)或数据丢失(JDK 8+);TreeMap并发修改会抛ConcurrentModificationException或产生不一致状态。
别误以为“TreeMap更‘正规’就更安全”——它和HashMap一样,只是普通非同步实现。
- 简单场景:用
Collections.synchronizedMap(new HashMap())或Collections.synchronizedSortedMap(new TreeMap()),但注意迭代仍需手动同步 - 高并发读多写少:用
ConcurrentHashMap(注意它不保证顺序,也不支持TreeMap的范围操作) - 需要线程安全+有序:没有直接对应类,得用
ConcurrentSkipListMap(基于跳表,行为接近TreeMap,支持并发且有序)
内存占用与初始化参数差异明显
HashMap默认初始容量16,负载因子0.75,意味着第13个元素插入时触发扩容;TreeMap无容量概念,只有节点数,每个Entry对象额外携带红黑树指针(left/right/parent/color),内存开销比HashMap单个Node更大。
HashMap可通过构造函数指定initialCapacity和loadFactor减少扩容次数;TreeMap只有带Comparator的构造函数,无法预设“大小”。
- 已知要存1000个键值对?HashMap设
new HashMap(1024)比默认16更省CPU - TreeMap没有类似优化手段,但若key比较开销大(如长字符串逐字符比),自定义
Comparator里加缓存或提前截断能显著提速 - 序列化时,HashMap保存哈希表结构,TreeMap保存红黑树结构,反序列化后行为一致,但字节大小不同
真正难的是权衡:你要的“顺序”是插入顺序(该用LinkedHashMap)、访问顺序(accessOrder=true的LinkedHashMap),还是key逻辑顺序(才轮到TreeMap);而“快”在不同场景下指向哈希计算、内存局部性、还是GC压力——这些细节藏在具体业务数据特征里,没法靠背区别应付过去。










