
本文详解单链表按位置删除节点时常见的 nullpointerexception 根源,提供健壮、可复用的删除逻辑,涵盖边界校验、指针重置、链表构建分离等关键实践。
本文详解单链表按位置删除节点时常见的 nullpointerexception 根源,提供健壮、可复用的删除逻辑,涵盖边界校验、指针重置、链表构建分离等关键实践。
在 Java 中实现单链表节点删除时,若未妥善处理空指针与边界条件,极易触发 NullPointerException——正如示例中报错所示:Cannot read field "next" because "temp" is null。根本原因在于:遍历打印后 temp 已变为 null,后续却直接访问 temp.next,且链表构建逻辑错误地混入删除方法中,破坏了单一职责原则。
以下是一个结构清晰、鲁棒性强的完整解决方案:
✅ 正确做法:职责分离 + 边界防护
首先,将链表构建与删除逻辑彻底分离;其次,在删除前严格校验输入合法性(如 n ≤ 0、n 超出链表长度),并确保操作指针始终非空。
import java.util.*;
class ListNode {
int data;
ListNode next;
ListNode(int data) {
this.data = data;
this.next = null;
}
}
public class LinkedListDelete {
// 构建测试链表: 5 → 4 → 3 → 1
public static ListNode buildList() {
ListNode head = new ListNode(5);
head.next = new ListNode(4);
head.next.next = new ListNode(3);
head.next.next.next = new ListNode(1);
return head;
}
// 删除第 n 个节点(n 从 1 开始计数)
public static ListNode deleteAtPosition(ListNode head, int n) {
// 边界检查:空链表或非法位置
if (head == null || n <= 0) return head;
if (n == 1) return head.next; // 删除头节点
ListNode temp = head;
// 移动到待删节点的前驱(即第 n-1 个节点)
for (int i = 1; i < n - 1 && temp != null; i++) {
temp = temp.next;
}
// 若 temp 为 null,说明 n 超出链表长度
if (temp == null || temp.next == null) {
System.out.println("Warning: Position " + n + " is out of bounds.");
return head;
}
// 跳过第 n 个节点
temp.next = temp.next.next;
return head;
}
// 打印链表(不改变 temp 状态)
public static void printList(ListNode head) {
ListNode curr = head;
while (curr != null) {
System.out.print(curr.data);
curr = curr.next;
}
System.out.println();
}
public static void main(String[] args) {
ListNode head = buildList(); // 构建 5→4→3→1
System.out.print("Original: ");
printList(head);
head = deleteAtPosition(head, 2); // 删除第 2 个节点(即 4)
System.out.print("After deleting position 2: ");
printList(head); // 输出: 531
}
}⚠️ 关键注意事项
-
指针重置:每次遍历(打印/查找)后,必须重新赋值
curr = head,不可复用已走到末尾的temp。 -
头节点特判:删除第 1 个节点需直接返回
head.next,避免空指针。 -
循环终止条件:
for循环中加入temp != null防止越界访问。 -
防御性编程:调用
temp.next.next前,务必确认temp != null && temp.next != null。 -
方法职责单一:
deleteAtPosition()只负责删除,链表构建、打印等应由独立方法承担。
运行上述代码,输出为:
Original: 5431 After deleting position 2: 531
这不仅修复了原始错误,更建立了可维护、可扩展的链表操作范式——让代码既正确,又清晰。

















