Java的LinkedList是一个重要的集合类,实现了双向链表的数据结构。相比于ArrayList,LinkedList在某些场景下具有更高的性能优势。本文将深入解析Java LinkedList的原理,并通过自定义实现来帮助你理解其底层机制。
什么是LinkedList?
LinkedList是Java集合框架中的一个类,它实现了List和Deque接口。与ArrayList不同,LinkedList基于双向链表的数据结构,适用于频繁插入和删除操作的场景。
LinkedList的底层实现原理
双向链表
LinkedList使用双向链表来存储元素。每个节点包含一个元素和指向前后两个节点的引用。
节点的插入和删除
在双向链表中,插入和删除操作的时间复杂度为O(1),因为只需要调整相关节点的引用,而不需要像ArrayList那样移动大量元素。
随机访问
由于需要沿着链表遍历节点,LinkedList的随机访问时间复杂度为O(n),不如ArrayList的O(1)高效。
自定义LinkedList的实现
以下是一个自定义LinkedList的示例代码,帮助你理解其底层原理。
自定义LinkedList类
java
public class CustomLinkedList<E> {
private Node<E> head;
private Node<E> tail;
private int size;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// 添加元素到末尾
public void add(E e) {
Node<E> newNode = new Node<>(tail, e, null);
if (tail == null) {
head = newNode;
} else {
tail.next = newNode;
}
tail = newNode;
size++;
}
// 获取指定位置的元素
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
// 获取节点
Node<E> node(int index) {
if (index < (size >> 1)) {
Node<E> x = head;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
} else {
Node<E> x = tail;
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
return x;
}
}
// 删除指定位置的元素
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
// 删除节点
private E unlink(Node<E> x) {
E element = x.item;
Node<E> next = x.next;
Node<E> prev = x.prev;
if (prev == null) {
head = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
tail = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
return element;
}
// 检查索引
private void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
// 获取大小
public int size() {
return size;
}
}
测试自定义LinkedList
java
public class CustomLinkedListTest {
public static void main(String[] args) {
CustomLinkedList<String> list = new CustomLinkedList<>();
list.add("Apple");
list.add("Banana");
list.add("Orange");
System.out.println("Element at index 1: " + list.get(1));
System.out.println("Size of list: " + list.size());
list.remove(1);
System.out.println("Element at index 1 after removal: " + list.get(1));
System.out.println("Size of list after removal: " + list.size());
}
}
详细讲解自定义LinkedList的各个部分
构造函数
构造函数初始化了头节点和尾节点,并设置初始大小为0。
添加元素
添加元素时,会创建一个新节点并将其添加到链表末尾。
java
public void add(E e) {
Node<E> newNode = new Node<>(tail, e, null);
if (tail == null) {
head = newNode;
} else {
tail.next = newNode;
}
tail = newNode;
size++;
}
添加元素图解
假设链表初始为空,依次添加元素 "Apple"、"Banana" 和 "Orange"。
-
添加 "Apple" 时:
null <- [Apple] -> null
-
添加 "Banana" 时:
null <- [Apple] <-> [Banana] -> null
-
添加 "Orange" 时:
null <- [Apple] <-> [Banana] <-> [Orange] -> null
获取元素
通过索引获取元素,首先检查索引是否合法,然后遍历链表找到对应的节点。
java
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
获取节点
根据索引值从头或尾开始遍历链表,提高效率。
java
Node<E> node(int index) {
if (index < (size >> 1)) {
Node<E> x = head;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
} else {
Node<E> x = tail;
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
return x;
}
}
删除元素
删除元素时,首先找到对应的节点,然后调整前后节点的引用,并将节点置为null以便垃圾回收。
java
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
删除元素图解
假设链表当前状态如下,删除元素 "Banana"(即索引1的元素):
null <- [Apple] <-> [Banana] <-> [Orange] -> null
-
找到 "Banana" 的节点。
-
调整 "Apple" 和 "Orange" 节点的引用,使它们直接相连:
null <- [Apple] <-> [Orange] -> null
-
将 "Banana" 节点置为null,以便垃圾回收。
删除节点
具体的节点删除操作。
java
private E unlink(Node<E> x) {
E element = x.item;
Node<E> next = x.next;
Node<E> prev = x.prev;
if (prev == null) {
head = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
tail = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
return element;
}
检查索引
确保索引在合法范围内。
java
private void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
获取大小
获取链表的大小。
java
public int size() {
return size;
}
总结
通过自定义LinkedList,我们深入理解了LinkedList的底层实现原理,包括其双向链表结构、元素插入与删除操作以及访问元素的方式。理解这些底层原理,有助于我们在使用LinkedList时更加高效和合理。
希望本文对你理解Java LinkedList的底层原理有所帮助。如果你喜欢这篇文章,请点赞并分享,关注我们的博客以获取更多关于Java编程和软件开发的精彩内容!