由于自己看源码时感觉难以看懂,即便看懂,隔一段时间回顾时也经常蒙圈,所以手写源码中的部分,达到模拟的目的,便于理解。
1、简介
LinkedList的数据结构是双向链表,下面将通过代码,模拟并介绍这种数据结构的特点。
2、模拟LinkedList的类名
public class LinkedListTest<E>
3、模拟LinkedList的成员变量
/**
* 双向链表大小
*/
private int size;
/**
* 双向链表头节点
*/
private Node<E> first;
/**
* 双向链表尾节点
*/
private Node<E> last;
4、模拟LinkedList的内部节点类
/**
* 双向链表节点类
*/
private static class Node<E> {
private E item;
private Node<E> next;
private Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
5、模拟LinkedList的add方法
/**
* 给双向链表添加元素
*
* @param e 元素
* @return
*/
public boolean add(E e) {
if (first == null) {
// 给空的双向链表添加元素
addNodeOnEmptyLinkedList(e);
} else {
// 给双向链表添加元素
addNodeOnLinkedList(e);
}
// 双向链表扩容,每次扩容1个长度
size++;
return true;
}
/**
* 给空的双向链表添加元素
*
* 这个方法包含以下两个动作:
* 1、创建新节点:新节点的pre节点、next节点都为空
* 2、双向链表的first成员变量、last成员变量,都指向新节点
*
* @param e
* @return
*/
private void addNodeOnEmptyLinkedList(E e) {
// 创建新节点:新节点的pre节点、next节点都为空
Node<E> newNode = new Node<>(null, e, null);
// 双向链表的first成员变量、last成员变量,都指向新节点
first = last = newNode;
}
/**
* 给双向链表添加元素
*
* 这个方法包含以下三个动作:
* 1、创建新节点,新节点的pre节点为双向链表旧的last成员节点,新节点的next节点为空
* 2、双向链表的last成员变量更改指针指向:由指向旧last节点,改成指向新节点
* 3、双向链表旧的last成员节点,其next指针指向新节点
*
* @param e
* @return
*/
private void addNodeOnLinkedList(E e) {
// 临时变量指向双向链表旧的last节点
Node<E> oldLast = last;
// 创建新节点,新节点的pre节点为双向链表旧的last成员节点,新节点的next节点为空
Node<E> newNode = new Node<>(oldLast, e, null);
// 双向链表的last成员变量更改指针指向:由指向旧last节点,改成指向新节点
last = newNode;
// 双向链表旧的last成员节点,其next指针指向新节点
oldLast.next = newNode;
}
6、模拟LinkedList的get方法
/**
* 从双向链表中查询元素
*
* @param index 查询元素的下标
* @return
*/
public E get(int index) {
// 校验下标是否合理
if (index < 0 || index >= size) {
return null;
}
// 寻找节点
Node<E> findNode;
// 若查询下标小于双向链表的一半大小,则从头部开始查,否则,从尾部开始查
if (index < size / 2) {
findNode = first;
for (int i = 0; i < index; i++) {
findNode = findNode.next;
}
} else {
findNode = last;
for (int i = size - 1; i > index; i--) {
findNode = findNode.prev;
}
}
return findNode.item;
}
7、模拟LinkedList使用add、get方法
/**
* 使用LinkedListTest,并打印结果
*
* @param args
*/
public static void main(String[] args) {
LinkedListTest<String> linkedListTest = new LinkedListTest<>();
linkedListTest.add("1");
linkedListTest.add("2");
linkedListTest.add("3");
linkedListTest.add("4");
linkedListTest.add("5");
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < linkedListTest.size; i++) {
sb.append(linkedListTest.get(i)).append(",");
}
sb.delete(sb.length() - 1, sb.length()).append("]");
System.out.println(sb);
}
控制台输出:
[1,2,3,4,5]
从中可以看出LinkedList的特点:链表无长度限制,添加元素的开销小,访问中间元素的开销大。
8、附上完整代码
/**
* 模拟双向链表
*
* @author pengxin
* @date 2022/1/13 14:46
*/
public class LinkedListTest<E> {
/**
* 双向链表大小
*/
private int size;
/**
* 双向链表头节点
*/
private Node<E> first;
/**
* 双向链表尾节点
*/
private Node<E> last;
/**
* 双向链表节点对象
*/
private static class Node<E> {
private E item;
private Node<E> next;
private Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
/**
* 给双向链表添加元素
*
* @param e 元素
* @return
*/
public boolean add(E e) {
if (first == null) {
// 给空的双向链表添加元素
addNodeOnEmptyLinkedList(e);
} else {
// 给双向链表添加元素
addNodeOnLinkedList(e);
}
// 双向链表扩容,每次扩容1个长度
size++;
return true;
}
/**
* 从双向链表中查询元素
*
* @param index 查询元素的下标
* @return
*/
public E get(int index) {
// 校验下标是否合理
if (index < 0 || index >= size) {
return null;
}
// 寻找节点
Node<E> findNode;
// 若查询下标小于双向链表的一半大小,则从头部开始查,否则,从尾部开始查
if (index < size / 2) {
findNode = first;
for (int i = 0; i < index; i++) {
findNode = findNode.next;
}
} else {
findNode = last;
for (int i = size - 1; i > index; i--) {
findNode = findNode.prev;
}
}
return findNode.item;
}
/**
* 给空的双向链表添加元素
*
* 这个方法包含以下两个动作:
* 1、创建新节点:新节点的pre节点、next节点都为空
* 2、双向链表的first成员变量、last成员变量,都指向新节点
*
* @param e
* @return
*/
private void addNodeOnEmptyLinkedList(E e) {
// 创建新节点:新节点的pre节点、next节点都为空
Node<E> newNode = new Node<>(null, e, null);
// 双向链表的first成员变量、last成员变量,都指向新节点
first = last = newNode;
}
/**
* 给双向链表添加元素
*
* 这个方法包含以下三个动作:
* 1、创建新节点,新节点的pre节点为双向链表旧的last成员节点,新节点的next节点为空
* 2、双向链表的last成员变量更改指针指向:由指向旧last节点,改成指向新节点
* 3、双向链表旧的last成员节点,其next指针指向新节点
*
* @param e
* @return
*/
private void addNodeOnLinkedList(E e) {
// 临时变量指向双向链表旧的last节点
Node<E> oldLast = last;
// 创建新节点,新节点的pre节点为双向链表旧的last成员节点,新节点的next节点为空
Node<E> newNode = new Node<>(oldLast, e, null);
// 双向链表的last成员变量更改指针指向:由指向旧last节点,改成指向新节点
last = newNode;
// 双向链表旧的last成员节点,其next指针指向新节点
oldLast.next = newNode;
}
/**
* 使用LinkedListTest,并打印结果
*
* @param args
*/
public static void main(String[] args) {
LinkedListTest<String> linkedListTest = new LinkedListTest<>();
linkedListTest.add("1");
linkedListTest.add("2");
linkedListTest.add("3");
linkedListTest.add("4");
linkedListTest.add("5");
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < linkedListTest.size; i++) {
sb.append(linkedListTest.get(i)).append(",");
}
sb.delete(sb.length() - 1, sb.length()).append("]");
System.out.println(sb);
}
}