昨天寫了簡單的自己的ArrayList類,今天寫寫LinkedList類。(因爲懶惰,原理就不寫了)
使用一個自己寫的結點類(包含了數據域和引用域)來實現相關操作。
構造MyLinkedList類
先寫結點類:
package MyLinkedList;
public class Node {
private Object data; //數據域
private Node next; //引用,指向下一個結點
//構造方法
public Node() {
// TODO Auto-generated constructor stub
}
//構造方法:傳入數據,置空next
public Node(Object data) {
this.data = data;
this.next = null;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
然後就可以構造鏈表類了
package MyLinkedList;
public class MyLinkedList {
private Node head; // 頭結點
private Node last; // 尾結點
private int size; // 結點長度
/**
* 構造方法:初始化頭結點和尾結點
*/
MyLinkedList() {
head = new Node();
last = head; // 初始狀態頭結點與尾結點指向同一個結點
}
}
重寫一個toString()方法
/**
* 規範鏈表輸出格式
*/
public String toString() {
StringBuffer sb = new StringBuffer();
Node current = head; // 從頭結點開始遍歷
while (true) {
if (current.getNext() != null) {// 不是最後一個結點時(next域不爲空)
current = current.getNext(); // 先指向下一個結點(因爲頭結點的數據爲空)
sb.append(current.getData()); // 取出數據
// 添加分隔符號
sb.append("->");
} else {
// 遍歷完了之後,退出循環
break;
}
}
// 去除最後一個分隔符
sb.deleteCharAt(sb.length() - 1);
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
添加操作add()
我重載了兩個添加操作的方法,一個是直接添加數據到鏈表尾部;另一個是在保證不越界的情況下,可以往鏈表的中間位置插入結點。
第一個添加操作:
/**
* 增加操作
*
* @param obj 要添加的結點數據
*/
public void add(Object obj) {
// 先創建一個結點
Node node = new Node(obj);
// 把新建的結點加入到鏈表中
last.setNext(node);
// 更改last的指向
last = node;
// 更改結點長度
size++;
}
查看效果:
package MyLinkedList;
public class TestMyLinkedList {
public static void main(String[] args) {
// TODO Auto-generated method stub
//創建鏈表
MyLinkedList myInt =new MyLinkedList();
myInt.add(1);
myInt.add(2);
myInt.add(3);
myInt.add("a");
System.out.println(myInt.toString());
}
}
注:因爲類中數據類型爲Object,可以爲任何類型的數據,且一個鏈表中數據類型可以多樣。
若想要鏈表數據類型任意但是統一,可以使用泛型。
第二種添加操作(插入)
/**
* 插入操作
*
* @param desIndex 插入的目標位置下標
* @param obj 插入的結點數據
*/
public void add(int desIndex, Object obj) {
// 判斷下標是否合法
if (desIndex < 0 || desIndex > size+1) { //(+1是因爲,允許插入到尾部)
try {
throw new Exception("下標越界");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
// 其他地方可以正常插入,先創建一個結點
Node node = new Node(obj);
// 插入到頭結點後一個位置
if (desIndex == 0) {
// 先把新結點的next指向原本頭結點指向的位置
node.setNext(head.getNext());
// 使頭結點的next指向新建立的結點
head.setNext(node);
// size增加
size++;
}
// 其他位置
else {
// 首先需要從頭遍歷找到要插入位置的前一個結點
Node before = head;
for (int i = 0; i < desIndex; i++) {
before = before.getNext();
}
// 循環結束,找到了before的位置
// 先把新建的結點的next指向before的next結點
node.setNext(before.getNext());
// 將before的next指向新創建的結點
before.setNext(node);
// 若創建的結點是最後一個,需要重新設置last
if (desIndex == size) {
last = node;
}
}
}
}
效果查看:
異常測試:
刪除操作remove()
刪除操作重載了兩個,一個是根據下標進行刪除,一個是傳入值,找到該值進行刪除
根據下標刪除:
/**
* 刪除操作
*
* @param desIndex 需要刪除的結點位置
*/
public void remove(int desIndex) {
// 判斷位置下標是否合法
if (desIndex < 0 || desIndex > size) {
try {
throw new Exception("下標越界");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
Node before = head; // 需要刪除的前一個結點
// 先找到before結點的位置
for (int i = 0; i < desIndex; i++) {
before = before.getNext();
}
// 循環結束,找到了before的位置
// 將before的next指向要刪除的下一個
before.setNext(before.getNext().getNext());
// 將要刪除的結點的next置空
// before.getNext().setNext(null);
// size減少
size--;
// 若刪除的是最後一個結點,需要更改last
if (desIndex == size) {
last = before.getNext();
}
}
}
效果查看:
第二種,根據值查找刪除(其中使用到的search查找方法是自己寫的,在下一個部分)
/**
* 刪除操作
* @param obj 需要刪除的結點數據
*/
public void remove(Object obj) {
// 獲得要刪除的元素位置下標
int index = search(obj);
if (index != -1) {
// 直接調用前面的方法
remove(index);
} else {
System.out.println("沒有找到你要刪除的元素");
}
}
效果查看:
myInt.remove((Integer)3);
異常測試:
查找操作search()
在鏈表中查找某數據,返回位置下標。若沒有找到,返回-1
/**
* 查找操作
*
* @param obj 需要查找的結點數據
* @return 返回結點位置下標。若沒有找到,返回-1
*/
public int search(Object obj) {
int index = -1; // 記錄下標
Node node = head;
// 遍歷鏈表進行查找
for (int i = 0; i < size; i++) {
node = node.getNext();
// 判斷
if (node.getData() == obj) {
index = i;
break; // 退出循環
}
}
return index;
}
效果:
System.out.println(myInt.search(3));
修改操作set()
修改也重載了兩個。一個根據下標修改,一個根據原始值查找修改。
根據下標修改:
/**
* 更改操作
* @param desIndex 需要更改的結點位置下標
* @param obj 需要更改的結點新數據
*/
public void set(int desIndex, Object obj) {
// 判斷位置下標是否合法
if (desIndex < 0 || desIndex > size) {
try {
throw new Exception("下標越界");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
Node node=head; //獲得要刪除的結點
//遍歷獲得
for(int i=0;i<=desIndex;i++) {
node=node.getNext();
}
//更改數據
node.setData(obj);
}
}
效果:
根據值查找修改: