教你如何使用Java手写一个基于数组实现的队列

一、概述
  队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。

  在Java中队列又可以分为两个大类,一种是阻塞队列和非阻塞队列。

  1、没有实现阻塞接口:

  1)实现java.util.Queue的LinkList,

  2)实现java.util.AbstractQueue接口内置的不阻塞队列: PriorityQueue 和 ConcurrentLinkedQueue

  2、实现阻塞接口的

  java.util.concurrent 中加入了 BlockingQueue 接口和五个阻塞队列类。它实质上就是一种带有一点扭曲的 FIFO 数据结构。不是立即从队列中添加或者删除元素,线程执行操作阻塞,直到有空间或者元素可用。
  五个队列所提供的各有不同:
   ArrayBlockingQueue :一个由数组支持的有界队列。
  
LinkedBlockingQueue :一个由链接节点支持的可选有界队列。
   PriorityBlockingQueue :一个由优先级堆支持的×××优先级队列。
  
DelayQueue :一个由优先级堆支持的、基于时间的调度队列。
  * SynchronousQueue :一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。
  队列是Java中常用的数据结构,比如在线程池中就是用到了队列,比如消息队列等。

  由队列先入先出的特性,我们知道队列数据的存储结构可以两种,一种是基于数组实现的,另一种则是基于单链实现。前者在创建的时候就已经确定了数组的长度,所以队列的长度是固定的,但是可以循环使用数组,所以这种队列也可以称之为循环队列。后者实现的队列内部通过指针指向形成一个队列,这种队列是单向且长度不固定,所以也称之为非循环队列。下面我将使用两种方式分别实现队列。

  二、基于数组实现循环队列
  由于在往队列中放数据或拉取数据的时候需要移动数组对应的下标,所以需要记录一下队尾和队头的位置。说一下几个核心的属性吧:

  1、queue:队列,object类型的数组,用于存储数据,长度固定,当存储的数据数量大于数组当度则抛出异常;

  2、head:队头指针,int类型,用于记录队列头部的位置信息。

  3、tail:队尾指针,int类型,用于记录队列尾部的位置信息。

  4、size:队列长度,队列长度大于等于0或者小于等于数组长度。

复制代码
/**

  • 队列管道,当管道中存放的数据大于队列的长度时将不会再offer数据,直至从队列中poll数据后
    */
    private Object[] queue;
    //队列的头部,获取数据时总是从头部获取
    private int head;
    //队列尾部,push数据时总是从尾部添加
    private int tail;
    //队列长度
    private int size;
    //数组中能存放数据的最大容量
    private final static int MAX_CAPACITY = 1<<30;
    //数组长度
    private int capacity;
    //最大下标
    private int maxIndex;
    复制代码
      

  三、数据结构

  图中,红色部分即为队列的长度,数组的长度为16。因为这个队列是循环队列,所以队列的头部不一定要在队列尾部前面,只要队列的长度不大于数组的长度就可以了。

  四、构造方法
  1、MyQueue(int initialCapacity):创建一个最大长度为 initialCapacity的队列。

  2、MyQueue():创建一个默认最大长度的队列,默认长度为16;

复制代码
public MyQueue(int initialCapacity){
if (initialCapacity > MAX_CAPACITY)
throw new OutOfMemoryError("initialCapacity too large");
if (initialCapacity <= 0)
throw new IndexOutOfBoundsException("initialCapacity must be more than zero");
queue = new Object[initialCapacity];
capacity = initialCapacity;
maxIndex = initialCapacity - 1;
head = tail = -1;
size = 0;
}
public MyQueue(){
queue = new Object[16];
capacity = 16;
head = tail = -1;
size = 0;
maxIndex = 15;
}
复制代码

  五、往队列添加数据
  添加数据时,首先判断队列的长度是否超出了数组的长度,如果超出了就添加失败(也可以设置成等待,等到有人消费了队列里的数据,然后再添加进去)。都是从队列的尾部添加数据的,添加完数据后tail指针也会相应自增1。具体实现如一下代码:

复制代码
/**

  • 往队列尾部添加数据
  • @param object 数据
    */
    public void offer(Object object){
    if (size >= capacity){
    System.out.println("queue‘s size more than or equal to array‘s capacity");
    return;
    }
    if (++tail > maxIndex){
    tail = 0;
    }
    queue[tail] = object;
    size++;
    }
    复制代码

  六、向队列中拉取数据
  拉取数据是从队列头部拉取的,拉取完之后将该元素删除,队列的长度减一,head自增1。代码如下:

复制代码
/**

  • 从队列头部拉出数据
  • @return 返回队列的第一个数据
    */
    public Object poll(){
    if (size <= 0){
    System.out.println("the queue is null");
    return null;
    }
    if (++head > maxIndex){
    head = 0;
    }
    size--;
    Object old = queue[head];
    queue[head] = null;
    return old;
    }
    复制代码
      

  七、其他方法
  1、查看数据:这个方法跟拉取数据一样都是获取到队列头部的数据,区别是该方法不会将对头数据删除:

复制代码
/**

  • 查看第一个数据
  • @return
    */
    public Object peek(){
    return queue[head];
    }
    复制代码
      2、清空队列:

复制代码
/**

  • 清空队列
    */
    public void clear(){
    for (int i = 0; i < queue.length; i++) {
    queue[i] = null;
    }
    tail = head = -1;
    size = 0;
    }
    复制代码
      完整的代码如下:

复制代码
/**

  • 队列
    */
    public class MyQueue {

    /**

    • 队列管道,当管道中存放的数据大于队列的长度时将不会再offer数据,直至从队列中poll数据后
      */
      private Object[] queue;
      //队列的头部,获取数据时总是从头部获取
      private int head;
      //队列尾部,push数据时总是从尾部添加
      private int tail;
      //队列长度
      private int size;
      //数组中能存放数据的最大容量
      private final static int MAX_CAPACITY = 1<<30;
      //数组长度
      private int capacity;
      //最大下标
      private int maxIndex;

    public MyQueue(int initialCapacity){
    if (initialCapacity > MAX_CAPACITY)
    throw new OutOfMemoryError("initialCapacity too large");
    if (initialCapacity <= 0)
    throw new IndexOutOfBoundsException("initialCapacity must be more than zero");
    queue = new Object[initialCapacity];
    capacity = initialCapacity;
    maxIndex = initialCapacity - 1;
    head = tail = -1;
    size = 0;
    }
    public MyQueue(){
    queue = new Object[16];
    capacity = 16;
    head = tail = -1;
    size = 0;
    maxIndex = 15;
    }

    /**

    • 往队列尾部添加数据
    • @param object 数据
      */
      public void offer(Object object){
      if (size >= capacity){
      System.out.println("queue‘s size more than or equal to array‘s capacity");
      return;
      }
      if (++tail > maxIndex){
      tail = 0;
      }
      queue[tail] = object;
      size++;
      }

    /**

    • 从队列头部拉出数据
    • @return 返回队列的第一个数据
      */
      public Object poll(){
      if (size <= 0){
      System.out.println("the queue is null");
      return null;
      }
      if (++head > maxIndex){
      head = 0;
      }
      size--;
      Object old = queue[head];
      queue[head] = null;
      return old;
      }

    /**

    • 查看第一个数据
    • @return
      */
      public Object peek(){
      return queue[head];
      }

    /**

    • 队列中存储的数据量
    • @return
      */
      public int size(){
      return size;
      }

    /**

    • 队列是否为空
    • @return
      */
      public boolean isEmpty(){
      return size == 0;
      }

    /**

    • 清空队列
      */
      public void clear(){
      for (int i = 0; i < queue.length; i++) {
      queue[i] = null;
      }
      tail = head = -1;
      size = 0;
      }

    @Override
    public String toString() {
    if (size <= 0) return "{}";
    StringBuilder builder = new StringBuilder(size + 8);
    builder.append("{");
    int h = head;
    int count = 0;
    while (count < size){
    if (++h > maxIndex) h = 0;
    builder.append(queue[h]);
    builder.append(", ");
    count++;
    }
    return builder.substring(0,builder.length()-2) + "}";
    }
    }
    复制代码

原文地址:http://blog.51cto.com/14084556/2320782

时间: 2024-10-10 20:43:46

教你如何使用Java手写一个基于数组实现的队列的相关文章

利用SpringBoot+Logback手写一个简单的链路追踪

目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简单的链路追踪,下面详细介绍下. 一.实现原理 Spring Boot默认使用LogBack日志系统,并且已经引入了相关的jar包,所以我们无需任何配置便可以使用LogBack打印日志. MDC(Mapped Diagnostic Context,映射调试上下文)是log4j和logback提供的一种

手把手教你搭建caffe及手写数字识别(全程命令提示、纯小白教程)

手把手教你搭建caffe及手写数字识别 作者:七月在线课程助教团队,骁哲.小蔡.李伟.July时间:二零一六年十一月九日交流:深度学习实战交流Q群 472899334,有问题可以加此群共同交流.另探究实验背后原理,请参看此课程:11月深度学习班. 一.前言 在前面的教程中,我们搭建了tensorflow.torch,教程发布后,大家的问题少了非常多.但另一大框架caffe的问题则也不少,加之caffe也是11月深度学习班要讲的三大框架之一,因此,我们再把caffe的搭建完整走一遍,手把手且全程命

放弃antd table,基于React手写一个虚拟滚动的表格

缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反而有所上升. 客观地说,antd是开源的,UI设计得比较美观(甩出其他组件库一条街),而且是蚂蚁金服的体验技术部(一堆p7,p8,p9,基本都是大牛级的)在持续地开发维护,质量可以信任. 不过,antd虽好,但一些组件在某一些场景下,是很不适用的.例如,以表格形式无限滚动地展示大量数据(1w+)时,

手写一个模块化的 TCP 服务端客户端

前面的博客 基于 socket 手写一个 TCP 服务端及客户端 写过一个简单的 TCP 服务端客户端,没有对代码结构进行任何设计,仅仅是实现了相关功能,用于加深对 socket 编程的认识. 这次我们对整个代码结构进行一下优化,使其模块化,易扩展,成为一个简单意义上的“框架”. 对于 Socket 编程这类所需知识偏底层的情况(OS 协议栈的运作机制,TCP 协议的理解,多线程的理解,BIO/NIO 的理解,阻塞函数的运作原理甚至是更底层处理器的中断.网卡等外设与内核的交互.核心态与内核态的切

&lt;数据结构系列1&gt;封装自己的数组——手写动态泛型数组

哈哈,距离上一次写博客已经快过去半个月了,这这这,好像有点慢啊,话不多说,开始我们的手写动态泛型数组 首先是我们自己写一个自己的动态数组类,代码如下所示: public class Array<E> { //成员变量:数据,大小 private E[] data; private int size; //构造函数,传入数组的容量capacity public Array(int capacity) { data=(E[])new Object[capacity]; size=0; } //无参

手把手写一个基于Spring Boot框架下的参数校验组件

手把手写一个基于Spring Boot框架下的参数校验组件(JSR-303) 前言 之前参与的新开放平台研发的过程中,由于不同的接口需要对不同的入参进行校验,这就涉及到通用参数的校验封装,如果不进行封装,那么写出来的校验代码将会风格不统一.校验工具类不一致.维护风险高等其它因素,于是我对其公共的校验做了一个封装,达到了通过注解的方式即可实现参数统一校验. 遇到的问题                    在封装的时候就发现了一个问题,我们是开放平台,返回的报文都必须是统一风格,也就是类似于{co

爬虫入门 手写一个Java爬虫

本文内容 涞源于  罗刚 老师的 书籍 << 自己动手写网络爬虫一书 >> ; 本文将介绍 1: 网络爬虫的是做什么的?  2: 手动写一个简单的网络爬虫; 1: 网络爬虫是做什么的?  他的主要工作就是 跟据指定的url地址 去发送请求,获得响应, 然后解析响应 , 一方面从响应中查找出想要查找的数据,另一方面从响应中解析出新的URL路径, 然后继续访问,继续解析;继续查找需要的数据和继续解析出新的URL路径  . 这就是网络爬虫主要干的工作.  下面是流程图: 通过上面的流程图

手写一个IOC容器

链接:https://pan.baidu.com/s/1MhKJYamBY1ejjjhz3BKoWQ 提取码:e8on 明白什么是IOC容器: IOC(Inversion of Control,控制反转).这是spring的核心,贯穿始终.所谓IOC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系. 传统的java代码中,我们需要使用哪个对象,就new一个对象,很正常对吧? 然而,这时出现了一个新思想:IOC(控制反转) 由它创建和管理所有的对象,我们需要的时

手写一个词法分析器

前言 最近大部分时间都在撸 Python,其中也会涉及到将数据库表转换为 Python 中 ORM 框架的 Model,但我们并没有找到一个合适的工具来做这个意义不大的"体力活",所以每次新建表后大家都是根据自己的表结构手写一遍 Model. 一两张表还好,一旦 10 几张表都要写一遍时那痛苦只有自己知道:这时程序员的 slogan 再次印证:一切毫无意义的体力劳动终将被计算机取代. intellij plugin 既然没有现成的工具那就自己写一个吧,演示效果如下: 考虑到我们主要是用