数据结构 - Queue

157 阅读5分钟

Queue的基本操作

一、Queue 的基本操作分类

graph TD
    A[Queue操作] --> B[添加操作]
    A --> C[移除操作]
    A --> D[查看操作]
    
    B --> B1[add]
    B --> B2[offer]
    
    C --> C1[remove]
    C --> C2[poll]
    
    D --> D1[element]
    D --> D2[peek]

二、详细操作说明

1. 添加操作
/**
 * Queue添加操作示例
 */
public class QueueAddOperations {
    Queue<String> queue = new LinkedList<>();
    
    // 1. add操作 - 添加失败抛出异常
    public void addExample() {
        try {
            queue.add("元素1");
            System.out.println("添加成功");
        } catch (IllegalStateException e) {
            System.out.println("队列已满,添加失败");
        }
    }
    
    // 2. offer操作 - 添加失败返回false
    public void offerExample() {
        boolean success = queue.offer("元素2");
        if (success) {
            System.out.println("添加成功");
        } else {
            System.out.println("队列已满,添加失败");
        }
    }
}
2. 移除操作
/**
 * Queue移除操作示例
 */
public class QueueRemoveOperations {
    Queue<String> queue = new LinkedList<>();
    
    // 1. remove操作 - 队列为空抛出异常
    public void removeExample() {
        try {
            String element = queue.remove();
            System.out.println("移除元素: " + element);
        } catch (NoSuchElementException e) {
            System.out.println("队列为空,移除失败");
        }
    }
    
    // 2. poll操作 - 队列为空返回null
    public void pollExample() {
        String element = queue.poll();
        if (element != null) {
            System.out.println("移除元素: " + element);
        } else {
            System.out.println("队列为空,无元素可移除");
        }
    }
}
3. 查看操作
/**
 * Queue查看操作示例
 */
public class QueuePeekOperations {
    Queue<String> queue = new LinkedList<>();
    
    // 1. element操作 - 队列为空抛出异常
    public void elementExample() {
        try {
            String element = queue.element();
            System.out.println("队首元素: " + element);
        } catch (NoSuchElementException e) {
            System.out.println("队列为空,无法查看");
        }
    }
    
    // 2. peek操作 - 队列为空返回null
    public void peekExample() {
        String element = queue.peek();
        if (element != null) {
            System.out.println("队首元素: " + element);
        } else {
            System.out.println("队列为空,无元素可查看");
        }
    }
}

三、操作对比表

graph LR
    A[操作类型] --> B[抛异常]
    A --> C[返回特殊值]
    
    B --> B1[add/插入]
    B --> B2[remove/删除]
    B --> B3[element/查看]
    
    C --> C1[offer/插入]
    C --> C2[poll/删除]
    C --> C3[peek/查看]

四、实际应用示例

/**
 * Queue实际应用示例
 */
public class QueueExample {
    
    // 1. 消息队列实现
    public class MessageQueue {
        private Queue<Message> messageQueue = new LinkedList<>();
        
        // 发送消息
        public void sendMessage(Message msg) {
            if (!messageQueue.offer(msg)) {
                handleQueueFullError();
            }
        }
        
        // 处理消息
        public void processMessage() {
            Message msg = messageQueue.poll();
            if (msg != null) {
                processMessageContent(msg);
            }
        }
    }
    
    // 2. 任务调度队列
    public class TaskScheduler {
        private Queue<Task> taskQueue = new PriorityQueue<>();
        
        // 添加任务
        public void addTask(Task task) {
            taskQueue.offer(task);
        }
        
        // 执行任务
        public void executeTasks() {
            while (!taskQueue.isEmpty()) {
                Task task = taskQueue.poll();
                executeTask(task);
            }
        }
    }
}

五、常见Queue实现类

/**
 * 不同Queue实现的特点
 */
public class QueueImplementations {
    
    // 1. LinkedList - 普通队列
    Queue<String> linkedList = new LinkedList<>();
    
    // 2. PriorityQueue - 优先级队列
    Queue<String> priorityQueue = new PriorityQueue<>();
    
    // 3. ArrayDeque - 双端队列
    Deque<String> arrayDeque = new ArrayDeque<>();
    
    // 4. BlockingQueue - 阻塞队列
    BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
}

六、性能对比

/**
 * 不同Queue实现的性能特点
 */
public class QueuePerformance {
    
    public void performanceComparison() {
        // LinkedList
        // - 插入删除 O(1)
        // - 内存占用较大
        Queue<Integer> linkedList = new LinkedList<>();
        
        // ArrayDeque
        // - 插入删除 O(1)
        // - 内存占用小
        Deque<Integer> arrayDeque = new ArrayDeque<>();
        
        // PriorityQueue
        // - 插入删除 O(log n)
        // - 自动排序
        Queue<Integer> priorityQueue = new PriorityQueue<>();
    }
}

七、使用建议

  1. 选择合适的实现
// 1. 普通FIFO队列
Queue<String> queue = new LinkedList<>();

// 2. 需要排序的队列
Queue<String> priorityQueue = new PriorityQueue<>();

// 3. 双端操作队列
Deque<String> deque = new ArrayDeque<>();

// 4. 线程安全队列
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
  1. 异常处理
/**
 * 安全的队列操作
 */
public class SafeQueueOperations {
    
    public void safeEnqueue(Queue<String> queue, String element) {
        try {
            if (!queue.offer(element)) {
                // 处理队列已满情况
                handleQueueFull();
            }
        } catch (Exception e) {
            // 处理其他异常
            handleException(e);
        }
    }
    
    public String safeDequeue(Queue<String> queue) {
        try {
            String element = queue.poll();
            if (element == null) {
                // 处理队列为空情况
                handleEmptyQueue();
            }
            return element;
        } catch (Exception e) {
            // 处理其他异常
            handleException(e);
            return null;
        }
    }
}

通过这些操作,我们可以:

  1. 安全地管理队列数据
  2. 选择合适的操作方法
  3. 处理各种异常情况
  4. 实现高效的队列操作

Queue在实际开发中使用非常广泛,特别是在:

  • 消息队列
  • 任务调度
  • 缓冲区管理
  • 数据流处理

相似操作的区别

让我详细对比每组相似操作的区别:

一、添加操作:add vs offer

/**
 * 添加操作对比
 */
public class AddOperationsComparison {
    
    public void compareAddOperations() {
        Queue<String> queue = new LinkedList<>();
        
        // 1. add操作
        try {
            queue.add("元素");
            // 特点:
            // - 添加成功返回true
            // - 队列已满时抛出IllegalStateException
            // - 适用于:确保添加成功的场景
        } catch (IllegalStateException e) {
            System.out.println("队列已满,add失败并抛出异常");
        }
        
        // 2. offer操作
        boolean success = queue.offer("元素");
        // 特点:
        // - 添加成功返回true
        // - 添加失败返回false
        // - 适用于:可以容忍添加失败的场景
        if (!success) {
            System.out.println("队列已满,offer失败返回false");
        }
    }
    
    // 实际应用场景
    public class MessageQueue {
        private Queue<String> queue = new LinkedList<>();
        
        // 使用add的场景:必须确保消息入队
        public void sendImportantMessage(String msg) {
            try {
                queue.add(msg);
                // 重要消息,必须入队成功
            } catch (IllegalStateException e) {
                // 触发告警,立即处理
                handleEmergency();
            }
        }
        
        // 使用offer的场景:允许消息丢失
        public void sendNormalMessage(String msg) {
            if (!queue.offer(msg)) {
                // 记录日志,稍后重试
                logAndRetryLater();
            }
        }
    }
}

二、移除操作:remove vs poll

/**
 * 移除操作对比
 */
public class RemoveOperationsComparison {
    
    public void compareRemoveOperations() {
        Queue<String> queue = new LinkedList<>();
        
        // 1. remove操作
        try {
            String element = queue.remove();
            // 特点:
            // - 返回并移除队首元素
            // - 队列为空时抛出NoSuchElementException
            // - 适用于:确保能获取元素的场景
        } catch (NoSuchElementException e) {
            System.out.println("队列为空,remove失败并抛出异常");
        }
        
        // 2. poll操作
        String element = queue.poll();
        // 特点:
        // - 返回并移除队首元素
        // - 队列为空时返回null
        // - 适用于:可以接受空值的场景
        if (element == null) {
            System.out.println("队列为空,poll返回null");
        }
    }
    
    // 实际应用场景
    public class TaskProcessor {
        private Queue<Task> taskQueue = new LinkedList<>();
        
        // 使用remove的场景:必须处理任务
        public void processImportantTask() {
            try {
                Task task = taskQueue.remove();
                processTask(task);
            } catch (NoSuchElementException e) {
                // 没有任务时触发告警
                notifyNoTasks();
            }
        }
        
        // 使用poll的场景:允许没有任务
        public void processNormalTask() {
            Task task = taskQueue.poll();
            if (task != null) {
                processTask(task);
            } else {
                // 空闲等待
                waitForNewTasks();
            }
        }
    }
}

三、查看操作:element vs peek

/**
 * 查看操作对比
 */
public class PeekOperationsComparison {
    
    public void comparePeekOperations() {
        Queue<String> queue = new LinkedList<>();
        
        // 1. element操作
        try {
            String element = queue.element();
            // 特点:
            // - 返回队首元素但不移除
            // - 队列为空时抛出NoSuchElementException
            // - 适用于:确保队列非空的场景
        } catch (NoSuchElementException e) {
            System.out.println("队列为空,element失败并抛出异常");
        }
        
        // 2. peek操作
        String peekElement = queue.peek();
        // 特点:
        // - 返回队首元素但不移除
        // - 队列为空时返回null
        // - 适用于:可以接受空值的场景
        if (peekElement == null) {
            System.out.println("队列为空,peek返回null");
        }
    }
    
    // 实际应用场景
    public class MessagePreview {
        private Queue<Message> messageQueue = new LinkedList<>();
        
        // 使用element的场景:必须预览消息
        public void previewImportantMessage() {
            try {
                Message msg = messageQueue.element();
                showPreview(msg);
            } catch (NoSuchElementException e) {
                // 触发告警
                notifyNoMessages();
            }
        }
        
        // 使用peek的场景:允许没有消息
        public void previewNormalMessage() {
            Message msg = messageQueue.peek();
            if (msg != null) {
                showPreview(msg);
            } else {
                showEmptyMessage();
            }
        }
    }
}

四、总结对比表

graph TB
    A[操作类型] --> B[抛异常操作]
    A --> C[返回特殊值操作]
    
    B --> B1[add]
    B --> B2[remove]
    B --> B3[element]
    
    C --> C1[offer]
    C --> C2[poll]
    C --> C3[peek]
    
    B1 --> D1[失败抛异常]
    B2 --> D2[空队列抛异常]
    B3 --> D3[空队列抛异常]
    
    C1 --> E1[失败返回false]
    C2 --> E2[空队列返回null]
    C3 --> E3[空队列返回null]

五、选择建议

  1. 使用抛异常的操作(add/remove/element)当:
  • 操作必须成功
  • 失败需要立即处理
  • 在确保队列状态的场景
  1. 使用返回特殊值的操作(offer/poll/peek)当:
  • 可以容忍操作失败
  • 需要更灵活的错误处理
  • 在不确定队列状态的场景
  1. 最佳实践:
// 1. 严格场景(如:关键业务处理)
try {
    queue.add(element);    // 必须添加成功
    queue.remove();        // 必须能够移除
    queue.element();       // 必须能够查看
} catch (Exception e) {
    // 立即处理异常
}

// 2. 宽松场景(如:普通业务处理)
if (queue.offer(element)) {  // 尝试添加
    String item = queue.poll();  // 尝试移除
    String peek = queue.peek();  // 尝试查看
}

这些操作的选择主要取决于:

  1. 业务的严格程度
  2. 错误处理的需求
  3. 代码的健壮性要求
  4. 性能的考虑