基于Map定义PriorityQueue时触发NullPointerException问题求助
PriorityQueue排序时出现NullPointerException的原因分析
你的代码片段
Map<Integer, Integer> map = new HashMap<>(); PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> map.get(b[1]) == map.get(a[1]) ? b[2] - a[2] : map.get(b[1]) - map.get(a[1])); List<List<Integer>> l = new ArrayList<>(); public TaskManager(List<List<Integer>> tasks) { for(List<Integer> t: tasks) { map.put(t.get(1), t.get(2)); pq.add(new int[]{t.get(0), t.get(1), t.get(2)}); } }
触发的错误
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
原因分析
PriorityQueue比较器的延迟执行特性
PriorityQueue不会在调用add()时完成全量排序,仅维护堆的基础有序状态。只有当执行poll()、peek()、队列扩容等操作时,才会触发比较器的调用。这意味着如果在add()元素后,map中的对应键被移除、或值被设为null,后续触发比较器时就会返回null,抛出NPE。不必要的外部依赖(核心问题)
你的队列元素int[]中已经携带了排序所需的t.get(2)(数组第三个元素),但你却选择通过外部map获取该值。这种冗余的依赖完全没必要,还把比较器逻辑和map强绑定,一旦map状态被意外修改(比如其他代码删除了键),就会触发异常。潜在的并发或键值问题
如果是多线程环境,多个线程同时修改map和操作pq,可能出现调用map.get()时键已被移除的情况;另外如果tasks中存在t.get(1)为null的情况,后续若删除map中的null键,也会导致map.get(null)返回null。
修复方案
最优方案:摆脱对外部map的依赖
直接使用队列元素自身携带的值进行排序,彻底消除NPE风险:
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> { // 先按原map对应的值降序排序(即数组第三个元素) int valueCompare = Integer.compare(b[2], a[2]); if (valueCompare != 0) { return valueCompare; } // 值相等时,按原逻辑用数组第三个元素降序(可根据实际需求调整) return b[2] - a[2]; });
若需保留map的其他用途
如果map还有其他查询需求,需确保:
- 整个
PriorityQueue生命周期内,map不会移除已存入的键,也不会将对应值设为null; - 多线程环境下,对
map和pq的操作加锁,或使用线程安全集合(如ConcurrentHashMap、PriorityBlockingQueue)。
内容的提问来源于stack exchange,提问作者Alexander Nenartovich
相关产品推荐
相关产品推荐

