位于上海,服务全国!

位于上海,服务全国!

探索Java的队列接口

作者:admin 分类: 时间:2017-03-27 17:05:59 点击量:616

Java的收集库提供了类和接口,以在处理之前根据指定的规范保存元素。 行为由它实现的数据结构特征来定义。 这些类是以保持通用元素的方式构建的;这意味着它可以保持任何但相同连续类型的元素,但在指定声明集合对象时。 Queue接口及其许多变量是其中之一。 本文描绘了工作概念以及在Java Collection API库中的后台运行的一系列API类。


概述
队列在其基本意义上是遵循特定存储和检索行为(称为FIFO或先进先出)的元素线性集合。 这意味着队列中元素的存储和检索顺序使得首先存储的元素是最先被检索的元素。 这种排序原则可以非常容易地通过站在队列中的人来类比地理解,例如站队购买公共汽车票。 站在前面的人首先拿票,并且先排队,而站在最后一排的人是最后出去的人。


图形1 排队
现在,你可能一直在思考他们的用途可能是什么,特别是在计算机系统中。 这里有几个。

想象一下具有单个处理器的计算机。 因此,它一次只能服务一个应用程序。 需要处理器时间的每个应用程序都放在队列中。 在队列前面的应用程序获得第一次被服务的机会。
队列也可用于打印假脱机,其中打印作业以队列提交。 因此,虽然提交了多个打印作业,但打印机从队列中拾取最近的一个。
在网络路由器中,信息包在队列中等待。 每次一个包到达一个节点时,它必须被路由到沿着路径的下一个节点,直到到达最终目的地。 路由节点一次路由一个数据包。 因此,其他节点排队等待,直到它可以被路由。

这个FIFO构思说明了一般情况下队列的预测能力。 这个排序原则非常类似于实现List接口的LinkedList类。 如果我们创建一个队列,稍微改变它的行为到LIFO(后进先出),它就成为一个堆栈。 除此之外,还有许多变化,例如优先级队列,其中元素根据元素的自然排序或根据提供的比较器排序。 在那里,可以说队列通常以FIFO方式运转,但不一定如此。

队列接口
Java API库中的Queue接口是Collection接口的扩展。 因此,它具有集合的所有能力,并且还提供诸如插入,移除和检查队列中的元素的操作。

PriorityQueue类是Queue接口的具体实现之一,也是最常用的之一。 它根据元素的自然排序对元素进行排序,如由Comparable的compareTo()方法所指定。 我们还可以在创建过程中通过PriorityQueue构造函数提供Comparator对象。 这个类提供了根据底层数据排序方式插入数据的功能。 但是,根据队列的行为,任何删除数据都从前面开始。 在PriorityQueue中添加元素以优先级顺序发生,例如由提供的值表示最高优先级元素。 但是,在删除元素时,它是第一个被删除的元素。 插入任何空元素会抛出ClassCastException。 此外,由于它与Comparator的关联,在此队列中不允许任何不可比较的值。

PriorityQueue的初始大小可以在其构造期间提供,但是默认情况下,它是无界的,意思是其的容量随着元素被插入而自动增长;我们提供的初始容量并不重要。
最常见的PriorityQueue操作如下:

boolean offer(E e): 根据优先级顺序在适当的位置插入一个元素。
E poll(): 移除最高优先级的元素,将其放在队列的前面。
E peek(): 获取队列中最高优先级元素的引用,但不会移除它。
void clear(): 从队列中移除所有元素。
int size(): 给出队列中的元素数量,或队列大小。
PriorityQueue的问题是它不同步;因此,不推荐它用于多线程访问。 然而,Java提供了另一个类,称为PriorityBlockingQueue,它是同步的。

以下用一个非常基本的举例来说明PriorityQueue的工作原理。

package org.mano.example;


import java.util.PriorityQueue;

public class TestPriorityQueue {

   public static void main(String[] args){
      PriorityQueue<Integer> q=new PriorityQueue<>();
      q.offer(12);
      q.offer(99);
      q.offer(10);
      q.offer(7);
      System.out.println("Polling...");
      while(q.size()>0){
         System.out.println(q.peek());
         q.poll();
      }
   }
}
The PriorityBlockingQueue
PriorityBlockingQueue类不是PriorityQueue的子类,但提供相同的函数集,包括一些派生自BlockingQueue的额外函数。 此类具有阻塞检索操作,由其实现的BlockingQueue接口强加。 这意味着如果队列为空,阻塞效果将导致调用线程被阻塞。 线程保持阻塞,直到至少一个元素被添加到队列。 正因为如此,队列在逻辑上是无界的;尝试插入可能会失败,从而导致OutOfMemoryError。

The ArrayBlockingQueue
名称提供了线索: ArrayBlockingQueue使用数组来维护其的元素。 从其他队列实现中挑出这个类的想法是,在这里我们需要提供阵列在其创建期间的容量。 此外,与其他队列不同,一旦声明,此容量不能超过。 这意味着,如果线程试图向已经满的队列中添加一个元素,它将被阻塞,直到一个元素被移除以为新的元素腾出空间。 由于这个固有的线程安全特性,它在java.util.concurrent包中定义。

The LinkedBlockingQueue
另一个队列类,称为LinkedBlockingQueue,以略有不同的方式操作。 首先,它作为链表被执行。 其次,它能以有界和无界的方式起作用。 它的行为有界,像ArrayBlockingQueue,如果在其创建期间提供容量。 并且,如果在创建时未提供容量,则其会变得无界。 这个类也是线程安全的,在java.util.concurrent包中定义。

The ConcurrentLinkedQueue
ConcurrentLinkedQueue也是在java.util.concurrent包中定义的线程安全队列。 与其他队列的主要区别是,没有与此类的实例相关联的阻塞因素。

同步队列
SynchronousQueue是一个完整的阻塞队列,它阻止向队列中添加元素的所有尝试,除非它也接收到从队列中检索元素的请求,反之亦然。 这个队列不同于其他队列的实现,似乎不像队列一样。 它特别适用于需要以同步方式从一个线程到另一个线程的双向传输的情况。 应该很明显,它是个安全的线程,并在java.util.concurrent包中定义。

The DelayQueue
DelayQueue是另一个有趣的队列实现,其允许插入一个实现Delayed接口的元素对象。 插入的元素基于可以从队列中移除之前剩余的时间量来排序。 这表示元素可以移除的限定时间。 到期时间可以通过getDelay()方法重新取回,其返回小于或等于零的值。 但是,poll()方法可以用于移除未过期的元素,但是这些元素被视为普通元素,而不是延迟种类。


一个简单的举例
package org.mano.example;


import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;


public class DelayedDemo implements Delayed {


   private String name;
   private long delay;


   public DelayedDemo(String name, long delay) {
      super();
      this.name = name;
      this.delay = delay;
   }


   public String getName() {
      return name;
   }


   @Override
   public int compareTo(Delayed o) {
      return (int)(delay - o.getDelay(TimeUnit.SECONDS));
   }


   @Override
   public long getDelay(TimeUnit unit) {
      return TimeUnit.SECONDS.convert(delay, unit);
   }


   public static void main(String[] args) {
      DelayQueue<DelayedDemo> queue = new DelayQueue<>();
      DelayedDemo d= new DelayedDemo("Wake up in 30 seconds", 30);
      queue.add(d);
      d = new DelayedDemo("Wake up in 40 seconds", 40);
      queue.add(d);
   }
}
结论
队列类是Java API库提供的许多工具中的一部分。它提供的种类可能不详尽,但它填补了编程需求的一个重要角落。另一个重要的队列类称为Deque,这里我们没有介绍。它表示一个双端队列,其中可以在队列的两端插入和恢复。