
1. 泛型单向链表的基础结构
在java中实现一个单向链表,通常需要两个核心组件:表示链表节点的 listnode 类和管理链表整体的 llist2 类。为了提高代码的通用性,我们采用泛型来定义这些类,使其能够存储任意类型的数据。
1.1 ListNode:链表节点定义
ListNode
- val: 存储节点的值,其类型由泛型参数 E 决定。
- next: 指向下一个节点的引用,类型为 ListNode
。
class ListNode{ private final E val; // 使用final修饰,表示节点值一旦创建不可变 private ListNode next; public ListNode(E val, ListNode next){ this.val = val; this.next = next; } public E getVal(){ return val; } public ListNode getNext(){ return next; } public boolean hasNext() { return next != null; } public void updateNext(ListNode node){ this.next = node; } }
注意事项:
- val 字段使用 final 修饰,表明一旦节点创建,其存储的值就不应再改变,这是一种良好的不变性实践。
- updateNext 方法允许修改当前节点的 next 引用,这是链表操作中常见的需求。
- hasNext 方法提供了便捷的判断,用于在遍历时检查是否存在下一个节点。
1.2 LList2:链表管理器定义
LList2
class LList2{ private ListNode front; // 链表头部节点 private ListNode rear; // 链表尾部节点 public LList2(ListNode front, ListNode rear){ this.front = front; this.rear = rear; } // ... 其他方法 ... }
说明:
立即学习“Java免费学习笔记(深入)”;
- front 和 rear 提供了对链表两端的快速访问,使得在头部或尾部添加/删除操作的效率更高。
2. 链表操作方法实现与类型兼容性问题解析
实现链表的关键在于正确地处理节点之间的链接关系。本节将重点介绍 addFirst、addLast 和 print 方法的实现,并详细解析在使用泛型时可能遇到的类型兼容性错误。
2.1 addFirst(E obj):在头部添加元素
addFirst 方法用于在链表的头部添加一个新元素。其逻辑是创建一个新节点,使其指向当前的 front 节点,然后更新 front 为这个新节点。如果链表原本为空,新节点同时也是 rear 节点。
public void addFirst(E obj){
front = new ListNode<>(obj, front); // 新节点指向原front
if(rear == null){ // 如果链表为空,则新节点也是rear
rear = front;
}
}2.2 addLast(E obj):在尾部添加元素与类型错误解析
addLast 方法用于在链表的尾部添加一个新元素。其核心逻辑是创建一个新节点,然后让当前的 rear 节点指向这个新节点,并更新 rear 为新节点。需要注意的是,当链表为空时,新节点既是 front 也是 rear。
public void addLast(E obj){ // 注意:这里参数是E obj,而不是ListNode
ListNode newNode = new ListNode<>(obj, null); // 创建新节点
if(rear != null) {
rear.updateNext(newNode); // 原尾节点指向新节点
} else {
// 链表为空时,新节点既是front也是rear
front = newNode;
}
rear = newNode; // 更新rear为新节点
} 类型兼容性错误解析:incompatible types: ListNode
这个错误通常发生在调用 addLast 方法时,传递了错误的参数类型。例如,当 addLast 方法定义为 public void addLast(E obj),而我们尝试这样调用时:
// 假设 LList2myList = ...; myList.addLast(new ListNode (-2, null)); // 错误!
错误原因:addLast(E obj) 方法明确声明它期望一个 E 类型的参数,其中 E 代表的是链表存储的“值”的类型(例如 Integer)。然而,在上述错误调用中,我们传递了一个 ListNode
正确调用方式: 我们应该直接传递要添加的“值”本身,由 addLast 方法内部负责将这个值封装成一个 ListNode 对象。
// 假设 LList2myList = ...; myList.addLast(-2); // 正确!直接传递Integer值
代码示例:
public class SingleLinkedListWorkSheetQ2Q3 {
public static void main(String[] args) {
// 构建初始链表:3 -> 2 -> 1 -> 0
ListNode rearNode = new ListNode<>(0, null);
ListNode listHead = new ListNode<>(1, rearNode);
listHead = new ListNode<>(2, listHead);
listHead = new ListNode<>(3, listHead);
LList2 myList = new LList2<>(listHead, rearNode);
System.out.print("原始链表: ");
myList.printWhile(); // 第一次打印
myList.addLast(-2); // 正确调用 addLast 方法,添加值-2
System.out.print("添加-2后链表: ");
myList.printWhile(); // 第二次打印,预期输出 [3, 2, 1, 0, -2]
}
} 2.3 链表遍历与打印 (printWhile, printFor)
遍历链表是常见的操作,但需要注意不要在遍历过程中意外修改链表的 front 或 rear 指针。
不推荐的遍历方式(会修改 front): 直接操作 front 指










