一文详解「队列」,手撸队列的3种方法!

存储
前面我们介绍了栈(Stack),队列和栈是比较像的一种数据结构。我们可以想象有很多辆汽车正在通过单行道的隧道,所有车辆不能插队、不能掉头,先进来的车也先出去,我们可以把这种特征的数据结构称之为队列。

 [[347452]]

本文已收录至我的 Github《算法图解》系列:https://github.com/vipstone/algorithm

前面我们介绍了栈(Stack),队列和栈是比较像的一种数据结构。我们可以想象有很多辆汽车正在通过单行道的隧道,所有车辆不能插队、不能掉头,先进来的车也先出去,我们可以把这种特征的数据结构称之为队列。

 

 


队列也属于逻辑结构,所谓的物理结构是指可以将数据存储在物理空间中,比如数组和链表都属于物理数据结构;而逻辑结构则是用于描述数据间的逻辑关系的,它可以由多种不同的物理结构来实现,比如队列和栈都属于逻辑结构。

 

 

队列特性

队列中的元素必须是先进先出(First In First Out,FIFO)的,它有两个重要的方法:入队(enqueue)和出队(dequeue)。队列的入口端叫队尾(rear),出口端叫队头(front),如下图所示:

 

手撸队列

学习了队列的基本知识之后,接下来我们将使用代码来实现一个队列。

首先我们先使用数组来实现一个队列,它的结构如下图所示:

 

1.自定义队列—数组

  1. public class MyQueue<E> { 
  2.  
  3.     private Object[] queue; // 存储容器 
  4.     private int head; // 头部指针 
  5.     private int tail; // 尾部指针 
  6.     private int size; // 队列实际存储长度 
  7.     private int maxSize; // 最大容量 
  8.  
  9.     public MyQueue() { 
  10.         // 无参构造函数,设置默认参数 
  11.         this.maxSize = 10; 
  12.         this.head = 0; 
  13.         this.tail = -1; 
  14.         this.size = 0; 
  15.         this.queue = new Object[this.maxSize]; 
  16.     } 
  17.  
  18.     public MyQueue(int initSize) { 
  19.         // 有参构造函数,设置参数 
  20.         this.maxSize = initSize; 
  21.         this.head = 0; 
  22.         this.tail = -1; 
  23.         this.size = 0; 
  24.         this.queue = new Object[this.maxSize]; 
  25.     } 
  26.  
  27.     /** 
  28.      * 查询队头元素 
  29.      */ 
  30.     public E peek() throws Exception { 
  31.         if (size == 0) { 
  32.             throw new Exception("队列中暂无数据"); 
  33.         } 
  34.         return (E) this.queue[this.head]; 
  35.     } 
  36.  
  37.     /** 
  38.      * 入列 
  39.      */ 
  40.     public boolean offer(E e) throws Exception { 
  41.         if (tail >= (maxSize - 1)) { 
  42.             throw new Exception("添加失败,队列已满"); 
  43.         } 
  44.         this.queue[++tail] = e; 
  45.         size++; 
  46.         return true
  47.     } 
  48.  
  49.     /** 
  50.      * 出列 
  51.      */ 
  52.     public E poll() throws Exception { 
  53.         if (size == 0) { 
  54.             throw new Exception("删除失败,队列为空"); 
  55.         } 
  56.         size--; 
  57.         return (E) this.queue[head++]; 
  58.     } 
  59.  
  60.     /** 
  61.      * 代码测试 
  62.      */ 
  63.     public static void main(String[] args) throws Exception { 
  64.         MyQueue queue = new MyQueue(); 
  65.         queue.offer("Hello"); 
  66.         queue.offer("Java"); 
  67.         System.out.println(queue.peek()); 
  68.         queue.poll(); 
  69.         System.out.println(queue.poll()); 
  70.     } 

以上代码的执行结果如下:

  • Hello
  • Java

2.自定义队列—链表

用链表实现队列的数据结构如下图所示:

 

实现代码如下:

  1. public class QueueByLinked { 
  2.  
  3.     /** 
  4.      * 声明链表节点 
  5.      */ 
  6.     static class Node<E> { 
  7.         E item; // 当前的值 
  8.  
  9.         Node<E> next; // 下一个节点 
  10.  
  11.         Node(E e) { 
  12.             this.item = e; 
  13.         } 
  14.     } 
  15.  
  16.     private Node firstNode; // 队头元素 
  17.     private Node lastNode; // 队尾元素 
  18.     private int size; // 队列实际存储数量 
  19.     private int maxSize; // 队列最大容量 
  20.  
  21.     public QueueByLinked(int maxSize) { 
  22.         if (maxSize <= 0) throw new RuntimeException("队列最大容量不能为空"); 
  23.         // 默认初始化函数 
  24.         firstNode = lastNode = new Node(null); 
  25.         this.size = 0; 
  26.         this.maxSize = maxSize; 
  27.     } 
  28.  
  29.     /** 
  30.      * 判断队列是否为空 
  31.      */ 
  32.     public boolean isEmpty() { 
  33.         return size == 0; 
  34.     } 
  35.  
  36.     /** 
  37.      * 入列 
  38.      */ 
  39.     public void offer(Object e) { 
  40.         // 最大值效验 
  41.         if (maxSize <= size) throw new RuntimeException("队列已满"); 
  42.         Node node = new Node(e); 
  43.         lastNode = lastNode.next = node; // 设置最后一个节点和倒数第二个节点的 next 
  44.         size++; // 队列数量 +1 
  45.     } 
  46.  
  47.     /** 
  48.      * 出列 
  49.      */ 
  50.     public Node poll() { 
  51.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  52.         size--; // 队列数量 -1 
  53.         return firstNode = firstNode.next; // 设置并返回队头元素(第一个节点是 null,当前元素则为 Node.next) 
  54.     } 
  55.      
  56.     /** 
  57.      * 查询队头元素 
  58.      */ 
  59.     public Node peek() { 
  60.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  61.         return firstNode.next;  // 返回队头元素(第一个节点是 null,当前元素则为 Node.next) 
  62.     } 
  63.  
  64.     /** 
  65.      * 代码测试 
  66.      */ 
  67.     public static void main(String[] args) { 
  68.         QueueByLinked queue = new QueueByLinked(10); 
  69.         queue.offer("Hello"); 
  70.         queue.offer("JDK"); 
  71.         queue.offer("Java"); 
  72.         System.out.println(queue.poll().item); 
  73.         System.out.println(queue.poll().item); 
  74.         System.out.println(queue.poll().item); 
  75.     } 

以上代码的执行结果如下:

  • Hello
  • JDK
  • Java

3.扩展:使用 List 实现自定义队列

除了以上两种方式之外,我们还可以使用 Java 自身的数据结构来实现队列,比如 List,我们这里提供一个实现的思路(但并不建议在实际工作中使用),实现代码如下:

  1. import java.util.ArrayList; 
  2. import java.util.List; 
  3.  
  4. /** 
  5.  * 自定义队列(List方式) 
  6.  */ 
  7. public class QueueByList<E> { 
  8.  
  9.     private List value; // 队列存储容器 
  10.  
  11.     public QueueByList() { 
  12.         // 初始化 
  13.         value = new ArrayList(); 
  14.     } 
  15.  
  16.     /** 
  17.      * 判断队列是否为空 
  18.      */ 
  19.     public boolean isEmpty() { 
  20.         return value.size() == 0; 
  21.     } 
  22.  
  23.     /** 
  24.      * 入列 
  25.      */ 
  26.     public void offer(Object e) { 
  27.         value.add(e); 
  28.     } 
  29.  
  30.     /** 
  31.      * 出列 
  32.      */ 
  33.     public E poll() { 
  34.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  35.         E item = (E) value.get(0); 
  36.         value.remove(0); 
  37.         return item; 
  38.     } 
  39.  
  40.     /** 
  41.      * 查询队头元素 
  42.      */ 
  43.     public E peek() { 
  44.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  45.         return (E) value.get(0); 
  46.     } 
  47.  
  48.     /** 
  49.      * 代码测试 
  50.      */ 
  51.     public static void main(String[] args) { 
  52.         QueueByList queue = new QueueByList(); 
  53.         queue.offer("Hello"); 
  54.         queue.offer("JDK"); 
  55.         queue.offer("Java"); 
  56.         System.out.println(queue.poll()); 
  57.         System.out.println(queue.poll()); 
  58.         System.out.println(queue.poll()); 
  59.     } 

以上代码的执行结果如下:

  • Hello
  • JDK
  • Java

队列使用场景

队列的常见使用场景有:

  • 存储多线程中等待排队执行的任务;
  • 存储多线程公平锁中等待执行任务的线程;
  • 常见消息中间件的任务队列等。

总结

通过以上三种队列的实现方式我们可以看出,任意容器都是可以用来实现队列(Queue)的,只要保证队列的元素先进先出(FIFO),并且在实现类中需要包含队列的四个核心方法:入列、出列、查询队列是否为空、返回队头元素等,就可以称为实现了一个自定义的队列。

 

责任编辑:武晓燕 来源: Java中文社群
相关推荐

2019-08-23 12:12:49

MQ消息队列

2024-10-08 08:52:59

2023-12-15 09:45:21

阻塞接口

2020-11-02 08:18:11

队列数据

2021-10-20 07:18:51

Linux延时队列

2024-05-30 08:05:17

2024-04-28 08:14:29

C#队列Queue

2023-11-01 11:06:18

2023-12-04 16:24:23

2020-09-23 09:24:01

堆栈开发实现

2022-06-09 08:17:30

Python__new__

2021-04-20 08:32:51

消息MQ队列

2023-09-26 12:22:37

队列Python

2022-06-26 00:18:05

企业产品化变量

2021-02-11 09:01:32

CSS开发 SDK

2020-12-01 09:30:34

区块链

2022-08-18 15:52:13

开发者阿里云

2022-03-01 20:41:00

机器学习特征人工智能

2023-02-28 18:09:53

Javascript定时器

2021-05-11 11:05:43

SAL子查询
点赞
收藏

51CTO技术栈公众号