学习的最好方式就是写出来
欢迎光临我的个人小窝:http://wsfss.top
今天我们就手撸一个简单版的双向链表实现LinkedList吧
1、当然是先写节点类Node
package com.fss.util;
public class Node<E> {
// 当前节点元素
E value;
// 上一个节点
Node<E> prev;
// 下一个节点
Node<E> next;
public Node(E value, Node<E> next) {
this.value = value;
this.next = next;
}
public Node(E value, Node<E> prev, Node<E> next) {
this.value = value;
this.prev = prev;
this.next = next;
}
}
2、实现单向链表的添加元素、下标位置添加元素,移除下标位置元素、获取下标位置元素
package com.fss.util;
import java.util.AbstractSequentialList;
import java.util.ListIterator;
import java.util.NoSuchElementException;
public class LinkedList<T> extends AbstractSequentialList<T> {
// 链表长度
transient int size;
// 头部节点
transient Node<T> first;
// 尾部节点
transient Node<T> last;
/**
* 默认在尾部添加
*/
public boolean add(T t) {
linkLast(t);
return true;
}
/**
* 在头部添加
*/
public boolean addFirst(T t) {
linkBefore(t);
return true;
}
/**
* 获取下标位置的节点
*/
public T get(int index) {
checkIndex(index);
Node<T> cur = node(index);
return cur.value;
}
/**
* 在下标位置添加,下标位置的节点替换为新节点,原下标位置的节点为新节点的下一节点
* @return
*/
@Override
public T set(int index, T t) {
checkIndex(index);
size++;
Node<T> cur = first;
Node<T> newNode;
// 头部插入节点
if (index == 0) {
newNode = new Node<>(t, cur, cur.next);
first = newNode;
newNode.next = cur;
cur.prev = newNode;
return t;
}
int position = 0;
while (cur.next != null) {
if (++position == index) {
newNode = new Node<>(t, cur, cur.next);
cur.next = newNode;
}
cur = cur.next;
}
return t;
}
/**
* 移除下标位置节点,原节点的上一节点的next=原节点的下一节点
*/
@Override
public T remove(int index) {
checkIndex(index);
Node<T> cur = node(index);
// 头部节点删除
if (cur.prev == null) {
first = cur.next;
} else {
cur.prev.next = cur.next;
}
size--;
return cur.value;
}
/**
* 打印链表信息
*/
public void print() {
System.out.println("双向链表的长度:" + size);
}
/**
* 获取链表长度
*/
@Override
public int size() {
return this.size;
}
@Override
public ListIterator<T> listIterator(int index) {
return new ListItr(index);
}
/**
* 在尾部添加节点
*/
private void linkLast(T t) {
final Node<T> temp = last;
// 新节点自动成为尾部节点
Node<T> newNode = new Node<>(t, temp, null);
last = newNode;
// 如果原来的尾部节点为空,表示是第一个节点,此时头部、尾部为同一节点
if (temp == null) {
first = newNode;
} else {
temp.next = newNode;
}
size++;
}
/**
* 在头部添加节点
*/
private void linkBefore(T t) {
final Node<T> temp = first;
Node<T> newNode = new Node<>(t, null, temp);
first = newNode;
// 如果原来的头部节点为空,表示是第一个节点,此时头部、尾部为同一节点
if (temp == null) {
last = newNode;
} else {
temp.prev = newNode;
}
size++;
}
/**
* 查找下标位置节点
*/
private Node<T> node(int index) {
int half = size >> 1;
// 下标小于链表长度的一半,从头部开始查找,否则从尾部查找
Node<T> cur;
if (index < half) {
cur = first;
for (int i=0; i<index; i++) {
cur = cur.next;
}
} else {
cur = last;
for (int i=(size -1); i>index; i--) {
cur = last.prev;
}
}
return cur;
}
/**
* 检查下标是否越界
*/
private void checkIndex(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("超过链表的长度范围");
}
}
/**
* 重写迭代器,实现单链表结构的迭代
* 这里只实现了迭代遍历元素,未实现迭代器的add等操作元素的方法
*/
private class ListItr implements ListIterator<T> {
// 上一节点
private Node<T> lastReturned;
// 下一节点
private Node<T> next;
// 下一节点下标
private int nextIndex;
public ListItr(int index) {
next = (index == size) ? null : node(index);
nextIndex = index;
}
@Override
public boolean hasNext() {
return nextIndex < size;
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.value;
}
@Override
public boolean hasPrevious() {
return false;
}
@Override
public T previous() {
if (!hasPrevious())
throw new NoSuchElementException();
return null;
}
@Override
public int nextIndex() {
return nextIndex;
}
@Override
public int previousIndex() {
return 0;
}
@Override
public void remove() {
}
@Override
public void set(T t) {
}
@Override
public void add(T t) {
}
}
}
3、写完了,测试一下
package test;
import com.fss.util.LinkedList;
import java.time.LocalDateTime;
public class LinkedListTest {
public static void main(String[] args) {
System.out.println("计时开始:" + LocalDateTime.now());
LinkedList<Integer> list = new LinkedList<>();
for (int i=1; i<9; i++) {
list.add(i);
}
System.out.println("计时结束:" + LocalDateTime.now());
list.print();
print(list);
System.out.println("在下标3位置添加元素:" + 0);
list.set(3, 0);
print(list);
System.out.println("移除下标3位置的元素");
list.remove(3);
print(list);
System.out.println("获取下标3位置的元素:" + list.get(3));
}
public static void print(LinkedList<Integer> list) {
for (Object i : list) {
System.out.print(i);
System.out.print(",");
}
System.out.println();
}
}
测试结果:
计时开始:2021-12-07T10:40:25.630
计时结束:2021-12-07T10:40:25.633
双向链表的长度:8
1,2,3,4,5,6,7,8,
在下标3位置添加元素:0
1,2,3,0,4,5,6,7,8,
移除下标3位置的元素
1,2,3,4,5,6,7,8,
获取下标3位置的元素:4