TreeMap 是 Java 中唯一能天然支持「按优先级自动排序 + 动态增删」的任务调度映射容器,前提是优先级能映射为可比较的 key;必须显式传入 Comparator(如 Comparator.naturalOrder()),否则自定义 key 未实现 Comparable 时会抛 ClassCastException,且自然排序易与业务语义冲突。

TreeMap 是 Java 中唯一能天然支持「按优先级自动排序 + 动态增删」的任务调度映射容器,前提是优先级能映射为可比较的 key —— 不能直接把 Task 对象当 key 塞进去就完事,否则会抛 ClassCastException 或行为错乱。
为什么必须用自定义 Comparator 而非自然序
任务优先级常见形式有:数值越小越紧急(如 0=最高)、字符串等级("CRITICAL" > "INFO")、甚至带时间戳的复合优先级。TreeMap 默认调用 compareTo(),若 key 是自定义类且没实现 Comparable,运行时直接报 NullPointerException 或 ClassCastException;即使实现了,也极可能和业务语义冲突(比如按对象哈希码排?)。
- 永远显式传入
Comparator,哪怕只是Comparator.naturalOrder() - 避免用
new TreeMap()无参构造,它只对String、Integer等内置类型安全 - 若优先级含时间维度(如“紧急度+提交时间”),务必在
compare()中先比紧急度,相等时再比时间,否则相同优先级的任务会因插入顺序不同而调度不一致
如何设计 key 类型避免装箱/拆箱与 null 风险
用 Integer 当 key 看似简单,但 put(null, task) 会触发 NullPointerException(TreeMap 明确禁止 key 为 null);用 int 又无法作为泛型参数。更糟的是,频繁任务调度场景下,Integer.valueOf(1) 的自动装箱会带来 GC 压力。
- 推荐用包装类 + 缓存策略:对小范围整数优先级(如 0–100),复用
Integer.valueOf()的缓存池;超出范围则自建IntKey不可变类,重写compareTo和hashCode - 绝对不要在 key 中存可变字段(如任务状态),key 一旦插入就不能再改,否则红黑树结构损坏,后续
get()可能找不到 - 如果优先级需动态调整,必须先
remove(oldKey)再put(newKey, task),不能原地修改
subMap / headMap / tailMap 在调度中的真实用途
很多人以为 subMap 只是“取一段”,其实它是调度器做批量预取或分级处理的核心:比如每轮只执行优先级 ≥5 的任务,或清理所有超时(优先级退化)任务。
-
map.subMap(5, true, Integer.MAX_VALUE, true)获取所有 ≥5 的任务视图,遍历时天然有序,且修改该视图会同步反映到原 map -
map.headMap(3, false)可快速定位并移除所有低优先级( - 注意边界参数含义:
true表示包含端点,false表示不包含;subMap(from, to)是左闭右开,容易漏掉to本身 - 这些方法返回的是视图(view),不是新拷贝,性能 O(1),但并发修改会触发
ConcurrentModificationException
线程安全与迭代过程中的 remove 操作
TreeMap 本身非线程安全,但调度器常需多线程协作(如一个线程添加任务,另一个线程取最高优执行)。直接套 Collections.synchronizedMap() 会锁整个 map,吞吐量暴跌;而用 ConcurrentSkipListMap 虽线程安全,却不支持 firstEntry() 这类原子获取+删除操作。
- 最简方案:用
synchronized(map)包裹firstEntry()+remove()组合操作,确保「取最高优并踢出」是原子的 - 严禁在 for-each 循环中调用
map.remove(key),会抛ConcurrentModificationException;改用Iterator.remove()或先收集 key 再批量删 - 如果调度频率极高(毫秒级),考虑用
PriorityQueue<task></task>配合外部锁,TreeMap 的 O(log n) 在百万级任务时仍可能成瓶颈
真正难的不是写对语法,而是想清楚:你的「优先级」到底是一个标量,还是需要多维比较的向量;以及,当两个任务优先级完全相同时,是否允许它们被调度器视为等价——这直接决定 comparator 里要不要引入插入序或时间戳作为决胜字段。

















