数据结构之队列

什么是队列Queue

什么是队列?
队列是数据结构中比较重要的一种类型(是一种数据结构),它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列),跟我们生活中的排队类似。
队列是一个有序列表,可以用数组或是链表来实现。使用数组的顺序队列,使用链表的为链式队列。
队列遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。
队列示意图
特点
队列的尾部添加数据的;拉取数据是从队列头部拉取的,拉取完之后将该元素删除。
使用场景
一般情况下,如果是对一些及时消息的处理,并且处理时间很短的情况下是不需要队列的,直接阻塞式的方法调用就可以了。但是如果在消息处理的时候特别费时间,这个时候如果有新消息来了,就只能处于阻塞状态,造成用户等待。这个时候便需要引入队列了。当接收到消息后,先把消息房贷队列中,然后再用行的线程进行处理,这个时候就不会有消息阻塞了。
队列主要应用在下面几种场景

• 队列用于异步数据传输(例如,数据不以两个进程之间的相同速率传输)。 管道,文件IO,套接字。

• 队列在大多数应用程序中用作缓冲区,如MP3媒体播放器,CD播放器等。

• 队列用于维护媒体播放器中的播放列表,以便添加和删除播放列表中的歌曲。
• 队列在操作系统中用于处理中断。

时间复杂性
队列时间复杂度

Queue的分类

  • 双端队列:双端队列(Deque)是 Queue 的子类也是 Queue 的补充类,头部和尾部都支持元素插入和删除.
  • 阻塞队列:在元素操作时(添加或删除),如果没有成功,会阻塞等待执行。例如,当添加元素时,如果队列元素已满,队列会阻塞等待直到有空位时再插入。
  • 非阻塞队列:非阻塞队列和阻塞队列相反,会直接返回操作的结果,而非阻塞等待。双端队列也属于非阻塞队列
    队列的继承关系图

Queue的基本方法

  • boolean add(E e) //将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。
  • E element() //获取,但是不移除此队列的头。
  • boolean offer(E e) //将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。
  • E peek() //获取但不移除此队列的头;如果此队列为空,则返回 null。
  • E poll() //获取并移除此队列的头,如果此队列为空,则返回 null。
  • E remove() //获取并移除此队列的头。删除元素,成功返回 true,失败返回 false;

Queue的实现

1 非阻塞队列

  • ConcurrentLinkedQueue:
    基于链接节点的、线程安全的无界队列。并发访问不需要同步。因为它在队列的尾部添加元素并从头部删除它们
    当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择
  • 优先级队列priorityQueue:
    一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素。非线程安全的,在多线程情况下可使用 PriorityBlockingQueue 类替

2 阻塞队列BlockingQueue

  • BlockingQueue 提供了线程安全的队列访问方式,当向队列中插入数据时,如果队列已满,线程则会阻塞等待队列中元素被取出后再插入;
  • 当从队列中取数据时,如果队列为空,则线程会阻塞等待队列中有新元素再获取
    不接受 null 元素 可以是限定容量的 主要用于生产者-使用者队列
    阻塞队列分为:
    ① LinkedBlockingQueue:
    是一个由链表实现的无界阻塞队列,内部维持着一链表构成的缓冲队列其容量默认值为 Integer.MAX_VALUE,也可以自定义容量,建议指定容量大小,变成有界阻塞队列,只有
    当缓冲区队列达到默认最大缓存容量Integer.MAX_VALUE或者指定的缓存容量时才会阻塞队列。其实现队列的锁是分离的来控制数据的同步,高并发可以提高整个队列的性能。即不同线程使用的锁不同
    ② PriorityBlockingQueue:
    一个支持优先级排序的无界阻塞队列,其内部控制线程的同步锁采用的是公平锁
    ③ ArrayBlockingQueue:
    是一个有边界的阻塞队列,它的内部实现是一个数组。它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变队列按 FIFO(先进先出)原则对元素进行排序,队列获取操作则是从队列头部开始获得元素, 内部的阻塞队列是通过可重入的互斥锁 ReentrantLock 和 Condition 条件队列实现的其锁没有实现分离不同的线程共用同一个锁,存在公平访问与非公平访问的区别,
    其内部还保存着两个整型变量。分别标识着队列的头部和尾部,插入和删除不会产生或者销毁任何的对象实例
    ④ DelayQueue:
    是一个支持延时获取元素的无界阻塞队列,队列中的元素必须实现 Delayed 接口,在创建元素时可以指定延迟时间,只有到达了延迟的时间之后,才能获取到该元素。实现了 Delayed 接口必须重写两个方法 ,getDelay(TimeUnit) 和 compareTo(Delayed) 此队列不允许使用 null 元素
    ⑤ SynchronousQueue:
    一个不存储元素、没有内部容量无缓冲等待的阻塞队列。其中每个插入操作必须等待另一个线程的对应移除操作。此队列不允许 null 元素;使用的数据结构是双重队列(Dual queue)和双重栈(Dual stack)
    ⑥ LinkedTransferQueue:
    一个由链表结构组成的无界阻塞TransferQueue队列。
    transfer方法。如果当前有消费者正在等待接收元素(消费者使用take()方法或带时间限制的poll()方法时),transfer方法可以把生产者传入的元素立刻transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
    tryTransfer方法。则是用来试探下生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回false。和transfer方法的区别是tryTransfer方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。

模拟队列

使用数组模拟队列

package DataStructure.Queue;

import java.util.Arrays;
import java.util.Scanner;

/**
 * @author Jerssy
 * @version V1.0
 * @Description 队列
 * @create 2021-02-06 14:46
 *
 * 队列介绍
 * 队列是一个有序列表,可以用数组或是链表来实现。支持FIFO,尾部添加、头部删除(先进队列的元素先出队列)
 *
 * 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
 *
 * 都是从队列的尾部添加数据的;拉取数据是从队列头部拉取的,拉取完之后将该元素删除
 *
 * 队列有2中实现方式:数组和链表
 * 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
 * 非环形队列--存在假溢出现象--队列满后,将全部元素出队却不能继续添加元素的情况
 */
public class ArrayQueue {
    public static void main(String[] args){
        Queue<String> queue = new Queue<>(3);
        Scanner scanner=new Scanner(System.in);
        label:while (true){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):获取队列数据");
            System.out.println("h(head):获取队列的头部数据");
            System.out.println("l(size):获取队列的长度");
            System.out.println("c(clear):清空队列");
            char c = scanner.next().charAt(0);
            try {
                switch (c){
                    case 's'-> queue.showQueue();
                    case 'e'-> {
                        break label;
                    }
                    case 'a'->{
                        System.out.println("请输入");
                        queue.addQueue(scanner.next());
                    }
                    case 'g'-> System.out.println("取出的数据为"+queue.popQueue());
                    case 'h'-> System.out.println("队列头部的数据为"+queue.peekQueue());
                    case 'l'->System.out.println("队列的长度为"+queue.size());
                    case 'c'->queue.clear();
                }
            }catch ( Exception e){
                e.printStackTrace();
            }
        }
    }


    static class   Queue< T>{
        private final int maxSize;//队列的最大容量,即数组长度
        private  int head;//队列的头
        private  int tail;//队列的尾部
        private final T[] arr;//存放数据,模拟队列
        private int size=0;  //队列长度

        //创建队列的构造器
        public  Queue(int arrMaxSize){
            if (arrMaxSize <= 0) throw new IndexOutOfBoundsException("arrMaxSize 不能为0");
            maxSize=arrMaxSize;

            arr= (T []) new Object[arrMaxSize];
            head=0;//指向队列的头部
            tail =0;//指向队列的尾部
        }
        //判断队列满
        public  boolean isFull(){

            return  size>=maxSize;
        }
        //判断队列为空
        public  boolean isEmpty(){
            return size==0;
        }
        //添加数据到队列
        public  void addQueue(T n){
            if (isFull()){
                throw  new RuntimeException("队列已满");
            }

            if (tail > maxSize-1){//改成循环队列
                tail = 0;
            }
            arr[tail++]=n;
            size++;
        }
        //数据出队列
        public T  popQueue() {
            if (isEmpty()){
                throw  new RuntimeException("队列为空");
            }
            if (head> maxSize-1){//改成循环队列
                head = 0;
            }
            size--;
           return   arr[head++];
        }
        //显示队列的所有数据
        public void  showQueue(){
            if (isEmpty()){
                throw  new RuntimeException("队列为空");
            }
            for (int i = head; i <head+size ; i++) {
                System.out.printf("arr[%s]=%s\n",i%maxSize, arr[i%maxSize]);
            }
        }
        //显示队列的头部
        public  T peekQueue(){
            if (isEmpty()){
                throw  new RuntimeException("队列为空");
            }
            return arr[head];
        }

        //队列的容量
        public int size(){
            return  size;
        }

        // 清空队列
        public void clear(){
            Arrays.fill(arr, 0);
            tail = head = 0;
            size = 0;
        }
    }
}

使用单链表模拟队列

package DataStructure.Queue;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author Jerssy
 * @version V1.0
 * @Description 链式队列
 * @create 2021-02-07 19:51
 */
public class LinkedQueue {

    public static void main(String[] args) {
        Queue<String> queue = new  Queue<>();
        Scanner scanner=new Scanner(System.in);
        label:while (true){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):获取队列数据");
            System.out.println("h(head):获取队列的头部数据");
            System.out.println("l(size):获取队列的长度");
            System.out.println("c(clear):清空队列");
            char c = scanner.next().charAt(0);
            try {
                switch (c){
                    case 's'-> queue.show();
                    case 'e'-> {
                        break label;
                    }
                    case 'a'->{
                        System.out.println("请输入");
                        queue.offer(scanner.next());
                    }
                    case 'g'-> System.out.println("取出的数据为"+queue.pop());
                    case 'h'-> System.out.println("队列头部的数据为"+queue.peek());
                    case 'l'->System.out.println("队列的长度为"+queue.size());
                    case 'c'->queue.clear();
                }
            }catch ( Exception e){
                e.printStackTrace();
            }
        }
    }

    static  class  Queue<T> {
      int size=0;
      private Node<T> head;
      private Node<T> tail;

        //入队列
        public void offer(T t){
           Node<T> node= new Node<>(t);
           if(isEmpty()){
               head=node;
           }
           else tail.next=node;
           tail=node;
           size++;
        }

        //出队列
        public T pop(){
            if(isEmpty()){
                 throw new RuntimeException("队列是空的");
            }
            T oldValue=head.item;
            if (head.next==null){//就一个元素
                head=tail=null;
            }
            else {
                Node<T> newNode=head.next;
                head.next=null;
                head = null;
                head=newNode;
            }
            size--;
            return  oldValue;
        }
       //判断队列是否为空
        public  boolean isEmpty(){
            return size == 0;
        }
		//返回队列的头部
        public  T peek(){
            return head.item;
        }
       //队列的长度
        public  int size(){
            return  size;
        }
		//打印队列
        public  void show(){
            if(isEmpty()){
                throw new RuntimeException("队列是空的");
            }

            Node<T> node=head;
            List<String> list = new ArrayList<>();
            while (node!= null){
                list.add((String) node.item);
                node = node.next;
            }
            System.out.println(String.join("-->", list));
        }
      //清空队列
        public void  clear(){
            for (Node<T> node= head; node != null; ) {
                 Node<T> next = node.next;
                 node.item = null;
                 node.next = null;
                 node = next;
            }
            head = tail = null;
            size = 0;
        }

    }
    //节点类
    static class   Node<T>{
         private T item;
         private  Node<T> next;
         
          public  Node(T item) {
              this.item = item;
          }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值