Deque的基本内容
一、Deque的基本概念 🎭
graph LR
A[头部操作] --> B[Deque]
C[尾部操作] --> B
B --> D[双向链表实现]
B --> E[数组实现]
二、基本实现 💻
/**
* Deque的基本实现(以双向链表为例)
*/
class SimpleDeque<T> {
// 节点定义
private class Node<T>(
var value: T,
var prev: Node<T>? = null,
var next: Node<T>? = null
)
private var head: Node<T>? = null
private var tail: Node<T>? = null
private var size = 0
/**
* 1. 头部操作
*/
fun addFirst(item: T) {
val newNode = Node(item)
if (isEmpty()) {
head = newNode
tail = newNode
} else {
newNode.next = head
head?.prev = newNode
head = newNode
}
size++
}
fun removeFirst(): T? {
if (isEmpty()) return null
val value = head?.value
head = head?.next
head?.prev = null
size--
if (isEmpty()) tail = null
return value
}
/**
* 2. 尾部操作
*/
fun addLast(item: T) {
val newNode = Node(item)
if (isEmpty()) {
head = newNode
tail = newNode
} else {
newNode.prev = tail
tail?.next = newNode
tail = newNode
}
size++
}
fun removeLast(): T? {
if (isEmpty()) return null
val value = tail?.value
tail = tail?.prev
tail?.next = null
size--
if (isEmpty()) head = null
return value
}
fun isEmpty() = size == 0
}
三、生动示例:餐厅排队系统 🏪
/**
* 餐厅排队系统
*/
class RestaurantQueue {
private val customerQueue = ArrayDeque<Customer>()
/**
* 1. VIP客人(从队首加入)
*/
fun addVipCustomer(customer: Customer) {
customerQueue.addFirst(customer)
println("VIP客人${customer.name}从队首加入")
}
/**
* 2. 普通客人(从队尾加入)
*/
fun addNormalCustomer(customer: Customer) {
customerQueue.addLast(customer)
println("普通客人${customer.name}从队尾加入")
}
/**
* 3. 紧急离开(从队首离开)
*/
fun leaveFromFront(): Customer? {
return customerQueue.pollFirst()?.also {
println("客人${it.name}从队首离开")
}
}
/**
* 4. 正常离开(从队尾离开)
*/
fun leaveFromEnd(): Customer? {
return customerQueue.pollLast()?.also {
println("客人${it.name}从队尾离开")
}
}
}
四、实际应用场景 🌟
/**
* 1. 浏览器历史记录
*/
class BrowserHistory {
private val history = ArrayDeque<String>()
// 添加新页面
fun visitPage(url: String) {
history.addLast(url)
}
// 后退
fun goBack(): String? {
return history.pollLast()
}
// 前进
fun goForward(): String? {
return history.peekFirst()
}
}
/**
* 2. 撤销重做系统
*/
class UndoRedoSystem {
private val undoStack = ArrayDeque<Action>()
private val redoStack = ArrayDeque<Action>()
// 执行操作
fun doAction(action: Action) {
action.execute()
undoStack.addLast(action)
redoStack.clear()
}
// 撤销
fun undo() {
undoStack.pollLast()?.let { action ->
action.undo()
redoStack.addLast(action)
}
}
// 重做
fun redo() {
redoStack.pollLast()?.let { action ->
action.execute()
undoStack.addLast(action)
}
}
}
五、性能特点 ⚡
/**
* Deque性能分析
*/
class DequePerformance {
/**
* 1. 时间复杂度
*/
fun timeComplexity() = """
双向链表实现:
- 头部操作:O(1)
- 尾部操作:O(1)
- 随机访问:O(n)
数组实现:
- 头部操作:O(1)(平均)
- 尾部操作:O(1)(平均)
- 随机访问:O(1)
- 扩容操作:O(n)
""".trimIndent()
/**
* 2. 空间复杂度
*/
fun spaceComplexity() = """
双向链表实现:
- 每个元素需要额外的前后指针空间
- 空间利用率相对较低
数组实现:
- 连续内存空间
- 可能存在空间浪费
- 需要预分配空间
""".trimIndent()
}
六、使用建议 💡
/**
* 使用建议
*/
class DequeUsageTips {
fun tips() = listOf(
// 1. 选择实现
"需要频繁随机访问时选择ArrayDeque",
"内存敏感时选择LinkedList实现",
// 2. 容量考虑
"预估容量大小,避免频繁扩容",
"注意内存使用限制",
// 3. 操作建议
"优先使用专用方法而不是通用List方法",
"注意空队列的处理",
"考虑线程安全需求"
)
}
七、常见误区 ⚠️
/**
* 常见误区
*/
class CommonMistakes {
fun mistakes() = """
1. 误区一:认为Deque就是队列
- Deque是双端队列,两端都可以操作
- 可以用作栈或队列
2. 误区二:忽视实现差异
- 不同实现性能特点不同
- 需要根据场景选择
3. 误区三:过度使用
- 简单场景可能Queue或Stack更合适
- 要根据实际需求选择
""".trimIndent()
}
总结一下:
-
Deque特点:
- 双端操作
- 灵活性强
- 实现方式多样
-
主要用途:
- 双向队列
- 栈结构
- 滑动窗口
-
使用建议:
- 根据场景选择实现
- 注意性能特点
- 合理使用API
ArrayDeque的底层实现
让我来解释 ArrayDeque 的底层实现原理:
// ArrayDeque 的基本结构
public class ArrayDeque<E> {
// 存储元素的数组,长度必须是 2 的幂
transient Object[] elements;
// 队列头部元素的索引
transient int head;
// 队列尾部下一个元素的索引
transient int tail;
}
🎯 核心特点:
- 循环数组实现
假设数组大小为8:
[_, _, A, B, C, D, _, _]
↑ ↑
head tail
数组索引:0 1 2 3 4 5 6 7
实际元素:_ _ A B C D _ _
- 添加元素示例:
// 初始状态
[_, _, A, B, C, D, _, _]
↑ ↑
head tail
// 添加元素 E
[_, _, A, B, C, D, E, _]
↑ ↑
head tail
// 添加元素 F
[_, _, A, B, C, D, E, F]
↑ ↑
head tail
// 继续添加 G,数组前面还有空间
[G, _, A, B, C, D, E, F]
↑ ↑
head tail
- 删除元素示例:
// 初始状态
[_, _, A, B, C, D, _, _]
↑ ↑
head tail
// 删除头部元素 A
[_, _, _, B, C, D, _, _]
↑ ↑
head tail
🔍 关键实现细节:
- 容量必须是2的幂
// 这样可以用位运算代替取模运算
index & (elements.length - 1) // 等价于 index % elements.length
- 扩容机制
// 当数组满时,容量翻倍
int newCapacity = elements.length << 1; // 左移1位,相当于乘2
- 主要操作
// 添加到队尾
public void addLast(E e) {
elements[tail] = e;
// 环形数组,如果到末尾需要回到开头
tail = (tail + 1) & (elements.length - 1);
}
// 添加到队首
public void addFirst(E e) {
head = (head - 1) & (elements.length - 1);
elements[head] = e;
}
// 从队首删除
public E pollFirst() {
E result = elements[head];
elements[head] = null;
head = (head + 1) & (elements.length - 1);
return result;
}
🎬 生活类比: 想象一个圆形自助餐传送带:
- 传送带是圆的(循环数组)
- 有进餐口(head)和出餐口(tail)
- 可以从两端放餐点或取餐点
- 当餐点太多时,换更大的传送带(扩容)
📝 优点:
- 双端操作都是 O(1) 时间复杂度
- 自动扩容
- 比 LinkedList 更节省内存
- 访问效率高(连续内存)
🚫 局限性:
- 不支持 null 元素
- 非线程安全
- 容量必须是 2 的幂
这就是 ArrayDeque 的核心实现原理。它通过巧妙的循环数组设计,实现了高效的双端队列操作。
详细操作
ArrayDeque 的完整核心方法实现:
public class ArrayDeque<E> {
// 存储元素的数组
transient Object[] elements;
// 头部索引
transient int head;
// 尾部索引
transient int tail;
// 最小初始容量
private static final int MIN_INITIAL_CAPACITY = 8;
// 构造方法
public ArrayDeque() {
elements = new Object[16]; // 默认容量16
}
// 添加元素到队尾
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ((tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity(); // 如果队列满了就扩容
}
// 添加元素到队首
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
head = (head - 1) & (elements.length - 1);
elements[head] = e;
if (head == tail)
doubleCapacity();
}
// 从队首删除并返回元素
public E pollFirst() {
int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];
if (result == null) // 队列为空
return null;
elements[h] = null; // 清除引用
head = (h + 1) & (elements.length - 1);
return result;
}
// 从队尾删除并返回元素
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
@SuppressWarnings("unchecked")
E result = (E) elements[t];
if (result == null) // 队列为空
return null;
elements[t] = null;
tail = t;
return result;
}
// 获取队首元素但不删除
public E peekFirst() {
@SuppressWarnings("unchecked")
E result = (E) elements[head];
return result;
}
// 获取队尾元素但不删除
public E peekLast() {
@SuppressWarnings("unchecked")
E result = (E) elements[(tail - 1) & (elements.length - 1)];
return result;
}
// 判断是否为空
public boolean isEmpty() {
return head == tail;
}
// 获取元素个数
public int size() {
return (tail - head) & (elements.length - 1);
}
// 清空队列
public void clear() {
int h = head;
int t = tail;
if (h != t) {
head = tail = 0;
int i = h;
int mask = elements.length - 1;
do {
elements[i] = null;
i = (i + 1) & mask;
} while (i != t);
}
}
// 扩容方法
private void doubleCapacity() {
int p = head;
int n = elements.length;
int r = n - p; // head右边的元素个数
int newCapacity = n << 1; // 新容量为原来的2倍
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
// 复制head右边的元素
System.arraycopy(elements, p, a, 0, r);
// 复制head左边的元素
System.arraycopy(elements, 0, a, r, p);
elements = a;
head = 0;
tail = n;
}
// 迭代器实现
public Iterator<E> iterator() {
return new DeqIterator();
}
private class DeqIterator implements Iterator<E> {
private int cursor = head;
private int fence = tail;
private int lastRet = -1;
public boolean hasNext() {
return cursor != fence;
}
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
@SuppressWarnings("unchecked")
E result = (E) elements[cursor];
lastRet = cursor;
cursor = (cursor + 1) & (elements.length - 1);
return result;
}
}
}
🔍 主要方法分类:
-
基本操作:
- addFirst/addLast:两端添加元素
- pollFirst/pollLast:两端移除元素
- peekFirst/peekLast:查看两端元素
-
状态查询:
- isEmpty:是否为空
- size:元素个数
- clear:清空队列
-
内部实现:
- doubleCapacity:扩容
- iterator:迭代器实现
🎯 关键实现特点:
- 循环数组:
[D, E, F, A, B, C]
↑ ↑
tail head
- 位运算优化:
// 使用位运算代替取模
index & (elements.length - 1) // 等价于 index % elements.length
-
自动扩容:
- 当 head 追上 tail 时进行扩容
- 新容量为原来的 2 倍
-
null 处理:
- 不允许存储 null 元素
- 移除元素后将位置设为 null 避免内存泄漏
这样的实现保证了:
- 两端操作都是 O(1) 时间复杂度
- 内存使用效率高
- 自动扩容机制
- 支持迭代操作
Deque 在 Android 和高并发中的应用
一、Android 中的应用 📱
/**
* 1. 事件总线实现
*/
class EventBus {
private val eventQueue = ConcurrentLinkedDeque<Event>()
// 发送事件
fun postEvent(event: Event) {
eventQueue.offerLast(event)
processEvents()
}
// 处理事件
private fun processEvents() {
while (!eventQueue.isEmpty()) {
eventQueue.pollFirst()?.let { event ->
dispatchEvent(event)
}
}
}
}
/**
* 2. 页面导航历史
*/
class NavigationHistory {
private val backStack = ArrayDeque<Fragment>()
// 打开新页面
fun navigate(fragment: Fragment) {
backStack.addLast(fragment)
showFragment(fragment)
}
// 返回上一页
fun goBack(): Boolean {
if (backStack.size <= 1) return false
backStack.removeLast()
val lastFragment = backStack.last()
showFragment(lastFragment)
return true
}
}
/**
* 3. 图片加载队列
*/
class ImageLoadQueue {
private val loadQueue = LinkedBlockingDeque<ImageTask>()
// 添加加载任务
fun addImageTask(task: ImageTask) {
loadQueue.offerLast(task)
}
// 优先加载任务
fun addPriorityTask(task: ImageTask) {
loadQueue.offerFirst(task)
}
}
二、高并发应用 🚀
/**
* 1. 生产者-消费者模式
*/
class ProducerConsumer {
private val queue = ConcurrentLinkedDeque<Task>()
// 生产者
class Producer(private val queue: ConcurrentLinkedDeque<Task>) : Runnable {
override fun run() {
while (true) {
val task = createTask()
queue.offerLast(task)
}
}
}
// 消费者
class Consumer(private val queue: ConcurrentLinkedDeque<Task>) : Runnable {
override fun run() {
while (true) {
queue.pollFirst()?.let { task ->
processTask(task)
}
}
}
}
}
/**
* 2. 任务调度系统
*/
class TaskScheduler {
private val highPriorityQueue = ConcurrentLinkedDeque<Task>()
private val normalPriorityQueue = ConcurrentLinkedDeque<Task>()
fun scheduleTask(task: Task) {
if (task.isHighPriority) {
highPriorityQueue.offerFirst(task)
} else {
normalPriorityQueue.offerLast(task)
}
}
fun processNextTask() {
// 优先处理高优先级任务
highPriorityQueue.pollFirst()?.let { task ->
executeTask(task)
return
}
// 处理普通任务
normalPriorityQueue.pollFirst()?.let { task ->
executeTask(task)
}
}
}
/**
* 3. 线程池工作队列
*/
class CustomThreadPool {
private val workQueue = LinkedBlockingDeque<Runnable>()
private val workers = mutableListOf<Worker>()
inner class Worker : Thread() {
override fun run() {
while (!isInterrupted) {
val task = workQueue.takeFirst() // 阻塞等待任务
try {
task.run()
} catch (e: Exception) {
// 处理异常
}
}
}
}
}
三、实际应用场景 🌟
/**
* 1. Android消息队列
*/
class MessageQueue {
private val messageDeque = ConcurrentLinkedDeque<Message>()
fun enqueueMessage(msg: Message, when: Long) {
// 根据时间戳插入合适位置
var p = messageDeque.peekFirst()
while (p != null && p.`when` <= when) {
p = messageDeque.peekFirst()
}
messageDeque.offerFirst(msg)
}
}
/**
* 2. 异步任务处理
*/
class AsyncTaskQueue {
private val taskQueue = LinkedBlockingDeque<AsyncTask<*,*,*>>()
fun execute(task: AsyncTask<*,*,*>) {
taskQueue.offerLast(task)
processNextTask()
}
private fun processNextTask() {
taskQueue.pollFirst()?.execute()
}
}
/**
* 3. 缓存管理
*/
class LruCache<K, V>(private val maxSize: Int) {
private val map = LinkedHashMap<K, V>()
private val accessQueue = ArrayDeque<K>()
fun put(key: K, value: V) {
if (accessQueue.size >= maxSize) {
// 移除最久未使用的项
val oldestKey = accessQueue.removeFirst()
map.remove(oldestKey)
}
map[key] = value
accessQueue.addLast(key)
}
}
四、性能优化建议 ⚡
/**
* 性能优化建议
*/
class PerformanceOptimization {
fun tips() = """
1. 选择合适的实现:
- ConcurrentLinkedDeque: 并发场景
- ArrayDeque: 单线程场景
- LinkedBlockingDeque: 需要阻塞操作时
2. 容量控制:
- 设置合理的初始容量
- 及时清理无用数据
- 避免无限增长
3. 并发处理:
- 使用线程安全的实现
- 避免过度同步
- 合理使用锁机制
""".trimIndent()
}
五、最佳实践 💡
/**
* 最佳实践建议
*/
class BestPractices {
fun recommendations() = listOf(
// 1. Android场景
"使用合适的Deque实现处理生命周期事件",
"实现可撤销操作的历史记录",
"管理异步任务队列",
// 2. 并发场景
"使用ConcurrentLinkedDeque避免同步开销",
"合理设置队列边界",
"实现优先级调度",
// 3. 性能优化
"预分配合适容量",
"及时释放资源",
"避免频繁扩容"
)
}
总结一下:
-
Android应用:
- 事件处理
- 页面导航
- 任务队列
- 缓存管理
-
高并发应用:
- 生产者-消费者模式
- 任务调度系统
- 线程池工作队列
- 消息队列
-
使用建议:
- 选择合适的实现
- 注意并发安全
- 控制容量大小
- 及时释放资源