双向链表需实现Iterable接口并自定义迭代器,在next()中通过current==head识别首节点、current==tail识别尾节点,配合NodeHandler回调封装首尾差异化逻辑。
要通过 for-each 遍历自定义双向链表并支持针对首尾节点的特殊逻辑,核心在于让链表类实现 iterable<t></t> 接口,并在迭代器中识别当前是否为头节点或尾节点。java 的增强 for 循环(for (t item : list))底层调用的是 iterator() 方法,因此需自定义迭代器行为,而非在循环体里手动判断首尾。
1. 双向链表需实现 Iterable 接口
你的链表类(如 DoublyLinkedList<t></t>)必须声明实现 Iterable<t></t>,并提供 iterator() 方法返回一个能感知位置的迭代器:
- 不直接在
for-each循环内用get(0)或size()-1判断首尾——双向链表通常不支持 O(1) 随机访问,且破坏遍历抽象 - 迭代器内部应持有对
head和tail的引用,或在首次/末次next()时标记状态 - 推荐在迭代器中维护一个
Node<t> current</t>和布尔标志(如isFirst,isLastNext),在hasNext()和next()中动态更新
2. 自定义迭代器中识别首尾节点
在 Iterator<t></t> 实现类中,可按如下逻辑区分节点角色:
-
首节点:第一次调用
next()时,current == head(且head != null) -
尾节点:下一次调用
hasNext()返回false时,本次next()返回的即为尾节点;也可在next()中判断current.next == null(即当前是 tail) - 若业务需要在遍历时“提前知道”是否为尾节点,可在
hasNext()中判断current != null && current.next != null,从而把“是否尾节点”的判断前移到循环条件中
3. 在 for-each 中触发差异化逻辑(推荐封装进迭代器)
不要在 for-each 循环体内写 if (i == 0) {...} else if (i == size-1) {...} —— 这违背了 Iterable 设计初衷,也难复用。更合理的方式是:
- 定义一个回调接口,例如
NodeHandler<t></t>,含onHead(T)、onBody(T)、onTail(T)三个方法 - 链表提供
forEachWithPosition(NodeHandler<t> handler)</t>方法,内部使用自定义迭代器驱动,并在每次next()时根据位置调用对应 handler 方法 - 这样业务代码保持简洁:
list.forEachWithPosition(new MyHandler()),无需关心索引或节点指针
4. 示例关键片段(简化版)
假设节点结构为 Node<t> { T data; Node<t> prev, next; }</t></t>,则迭代器核心逻辑类似:
private class LinkedIterator implements Iterator<T> {
private Node<T> current = head;
private boolean isFirst = true;
private final Node<T> tailRef = tail;
public boolean hasNext() {
return current != null;
}
public T next() {
if (!hasNext()) throw new NoSuchElementException();
T data = current.data;
// 标记首尾
if (isFirst) {
// 此时是头节点 → 可在此触发 onHead(data)
isFirst = false;
}
if (current == tailRef) {
// 此时是尾节点 → 可在此触发 onTail(data)
}
current = current.next;
return data;
}
}
再配合 forEachWithPosition 方法,就能在不暴露内部结构的前提下,安全、清晰地执行首尾定制逻辑。

















