Java自定义双向链表迭代器遍历末尾返回null的问题排查
双向链表迭代器遍历末尾返回null问题排查
问题根因
你的链表采用双哨兵节点设计,固定包含两个不存业务数据的占位节点:
- 头部哨兵
header:作为链表起始占位,避免空链表的边界判断 - 尾部哨兵
trailer:作为链表结尾占位,所有真实业务节点的next指针最终会指向它
当前迭代器的判断逻辑没有适配这个结构,错误地用current != null作为遍历终止条件:
@Override public boolean hasNext() { return current != null; }
这个逻辑存在明确漏洞:
遍历完所有真实业务元素后,
current指针会移动到trailer哨兵节点上
trailer本身是有效对象,并非null,因此hasNext()会错误返回true,触发一次额外的next()调用
由于trailer的data字段固定为null,这个多余的调用就会把null值写入结果集合,和你测试看到的[0, 1, 2, 3, 4,null]输出完全匹配。
具体时序可以对照验证:
- 迭代器初始化时,
current指向header的下一个节点(第一个真实元素) - 依次读取0、1、2、3、4五个元素,每读一个就把current向后移动一格
- 读完元素4后,current移动到trailer节点
- 此时执行
hasNext():current是trailer(非null),返回true - 执行
next():读取trailer的null值加入结果列表,再把current移动到trailer的next(也就是null),循环终止
修复方法
把迭代器的hasNext()判断逻辑改为识别尾部哨兵节点,而不是判断指针是否为null:
private class DoublyLinkedListIterator implements Iterator<E> { private Node<E> current; public DoublyLinkedListIterator() { current = header.getNext(); } @Override public boolean hasNext() { // 只要current还没走到尾部哨兵,就还有可遍历的真实元素 return current != trailer; } @Override public E next() { if(!hasNext()) throw new RuntimeException("无可用元素"); E res = current.getData(); current = current.getNext(); return res; } }
改完后,当current移动到trailer时,hasNext()会立刻返回false,不会触发多余的null读取。
代码中附带的潜在问题
顺带提两个你当前实现里容易踩坑的逻辑漏洞:
- 自定义
toString()的循环判断顺序存在空指针风险
你当前的循环判断是while (current.getNext() != null && current != null),先调用getNext()再判断current是否为null,一旦current为null就会直接抛NPE;同时这个判断没有排除trailer节点,和迭代器的问题一样,会把trailer的null值拼接到字符串里。正确的判断顺序应该先校验current非空,再排除尾部哨兵:
while (current != null && current != trailer) { // 拼接元素逻辑 }
add(int i, E e)方法的边界判断错误
你当前的判断是if (i < 0|| i > size()-1),实际链表插入的合法索引范围是0 <= i <= size(也就是允许在链表尾部插入),这个判断会导致尾部位置的合法插入操作被错误拦截。
内容的提问来源于stack exchange,提问作者mohelt
相关产品推荐
相关产品推荐

