LinkedList 是 Java 中的一个双向链表实现,它继承自 AbstractSequentialList 并实现了 List、Deque 接口。LinkedList 提供了双向链表的数据结构,支持快速的元素插入和删除操作,尤其是在列表的头部和尾部。它允许重复元素和 null 值,并且可以作为队列、栈或双端队列使用。由于其链表的特性,LinkedList 在进行元素添加和删除操作时具有较高的灵活性,但在随机访问操作上的性能不如 ArrayList。在多线程环境中,LinkedList 需要外部同步来保证线程安全。它适用于需要频繁插入和删除元素的场景,如实现 LIFO 栈或 FIFO 队列。
c LinkedList
LinkedList 在 Java 中是基于双向链表实现的,它包含多个节点,每个节点都包含数据和两个引用,分别指向前一个节点和后一个节点。以下是 LinkedList 的设计:
设计思考:
- 需求场景:
- 在许多编程任务中,需要一个可以快速进行插入和删除操作的动态数组。例如,在实现栈、队列、双向队列等数据结构时,这些操作非常常见。
- 现有技术局限性:
ArrayList提供了快速的随机访问能力,但在进行插入和删除操作时,可能需要移动数组中的大量元素,导致效率低下。Vector类似于ArrayList,但它是线程安全的,但使用synchronized进行同步,导致并发性能较差。
- 技术融合:
LinkedList结合了链表的插入和删除效率高的特点,并提供了双向链表的实现,允许从两端快速地添加或移除元素。
- 设计理念:
LinkedList通过使用链表结构,可以有效地进行插入和删除操作,因为这些操作仅需要改变节点的指针,而不需要移动整个数组。- 它还实现了
List接口,提供了与ArrayList相同的接口,但具有不同的性能特性。
- 实现方式:
LinkedList由一系列Node对象组成,每个Node包含数据和两个引用(previous和next),分别指向前一个和后一个节点。
2、 数据结构
以下是
LinkedList 数据结构的主要特点:
- 链式存储:元素在内存中不是连续存储的,而是通过指针(引用)连接起来的。
- 节点结构:每个节点至少包含两部分信息,一个是存储数据的元素,另一个是指向同链表中下一个节点的引用。在双向链表中,还会有一个指向前一个节点的引用。
- 动态大小:
LinkedList的大小是动态的,可以根据需要随时插入或删除节点。 - 允许空链表:可以创建一个不包含任何节点的空链表。
- 插入和删除效率高:在链表的任意位置插入或删除节点的操作时间复杂度为 O(1),因为这些操作只涉及到节点的引用的改动。
- 访问元素效率低:访问特定索引位置的元素需要从头节点开始遍历链表,时间复杂度为 O(n)。
- 没有空间浪费:与数组不同,链表不需要预先分配固定大小的存储空间。
- 有序性:链表中的节点按照它们被插入的顺序保持有序。
- 可以实现为双向或循环链表:标准的
LinkedList实现可以是双向的,也可以是循环的(尾节点指向头节点)。
3、 执行流程
图说明:
- 初始化 LinkedList:
- 创建一个空的
LinkedList实例。
- 创建一个空的
- 添加元素:
- 将新元素添加到
LinkedList。
- 将新元素添加到
- 删除元素:
- 从
LinkedList删除指定的元素。
- 从
- 访问元素:
- 根据索引访问
LinkedList中的元素。
- 根据索引访问
- 遍历 LinkedList:
- 通过节点间的链接顺序遍历整个
LinkedList。
- 通过节点间的链接顺序遍历整个
- 检查边界条件:
- 在执行索引相关操作前,检查索引是否在有效范围内。
- 获取节点:
- 获取指定索引处的节点。
- 更新节点指针:
- 在添加或删除元素时,更新节点间的指针。
- 返回节点数据:
- 返回指定节点的数据。
- LinkedList 节点:
LinkedList由一系列节点组成,每个节点包含前一个节点、后一个节点和节点数据。
- Node prev:
- 节点中保存的对前一个节点的引用。
- Node next:
- 节点中保存的对后一个节点的引用。
- Node data:
- 节点中保存的数据。
4、优点:
- 高效的插入/删除操作:
- 在任意位置插入和删除元素的时间复杂度为 O(1)。
- 允许空链表:
- 与
ArrayList不同,LinkedList允许空链表存在,不会因为空数组而抛出异常。
- 与
- 能够作为栈、队列和双端队列使用:
- 提供了实现栈、队列和双端队列所需的方法。
5、缺点:
- 随机访问性能差:
- 由于链表的结构,随机访问元素的时间复杂度为 O(n)。
- 内存开销:
- 相比于
ArrayList,LinkedList需要额外的内存来存储节点的指针。
- 相比于
6、使用场景:
- 需要快速插入和删除元素的场景,如实现一个消息队列。
- 作为栈、队列或双端队列使用。
7、类设计
8、应用案例
LinkedList 通常用于实现那些需要快速插入和删除元素的数据结构,如栈(Stack)和队列(Queue)。这是一个简单的任务调度系统,用于管理任务的执行顺序:
import java.util.LinkedList;
// 任务类,用于表示需要执行的任务
class Task implements Comparable<Task> {
private final String name;
private final int priority;
public Task(String name, int priority) {
this.name = name;
this.priority = priority;
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.priority, other.priority);
}
@Override
public String toString() {
return name + " (Priority: " + priority + ")";
}
}
public class TaskScheduler {
private final LinkedList<Task> taskQueue;
public TaskScheduler() {
taskQueue = new LinkedList<>();
}
// 添加任务到队列
public void addTask(Task task) {
taskQueue.add(task);
}
// 执行所有任务
public void executeTasks() {
while (!taskQueue.isEmpty()) {
Task task = taskQueue.poll(); // 取出优先级最高的任务
System.out.println("Executing task: " + task);
// 模拟任务执行时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
TaskScheduler scheduler = new TaskScheduler();
scheduler.addTask(new Task("Task 1", 3));
scheduler.addTask(new Task("Task 2", 1)); // 高优先级任务
scheduler.addTask(new Task("Task 3", 2));
// 执行所有任务
scheduler.executeTasks();
}
}