昨天写了简单的自己的ArrayList类,今天写写LinkedList类。(因为懒惰,原理就不写了)
使用一个自己写的结点类(包含了数据域和引用域)来实现相关操作。
先写结点类:
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();
}
我重载了两个添加操作的方法,一个是直接添加数据到链表尾部;另一个是在保证不越界的情况下,可以往链表的中间位置插入结点。
第一个添加操作:
/**
* 增加操作
*
* @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;
}
}
}
}
效果查看:
异常测试:
删除操作重载了两个,一个是根据下标进行删除,一个是传入值,找到该值进行删除
根据下标删除:
/**
* 删除操作
*
* @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);
异常测试:
在链表中查找某数据,返回位置下标。若没有找到,返回-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));
修改也重载了两个。一个根据下标修改,一个根据原始值查找修改。
根据下标修改:
/**
* 更改操作
* @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);
}
}
效果:
根据值查找修改: