Queue [Java]
来源:互联网 发布:深红累之渊 知乎 编辑:程序博客网 时间:2024/06/06 18:46
原文地址:http://blog.csdn.net/Victor_Jan/archive/2004/09/27/117695.aspx
Java1.5版本最终提供了对编程中最基础数据结构之一-Queue的内在支持。本文章将探究新添加到java.util包中的Queue接口,演示如何去使用这个新特性去使你的数据处理流式化。
(英文原文见http://www.devx.com/Java/Article/21983/1954?pf=true
在 计算机学科中,基础数据结构之一 —Queue。你会想起Queue是一种数据结构,在它里边的元素可以按照添加它们的相同顺序被移除。在以前的Java版本中,这中FIFO(先进先出)数据结构很不幸被忽略了。随着Java1.5(也叫Tiger)的出现,对Queue支持第一次成为固有特性。
过去在没有Queue的情况下如何管理?
在Java 1.5以前,通常的实现方式是使用java.util.List集合来模仿Queue。Queue的概念通过把对象添加(称为enqueuing的操作)到List的尾部(即Queue的后部)并通过从List的头部(即Queue的前部)提取对象而从List中移除(称为dequeuing的操作)来模拟。下面代码显示了你以前可能做法。
import java.util.Enumeration;import java.util.LinkedList;import java.util.Vector;public class QueueEmulate { private LinkedList queueMembers; public Object enqueue(Object element) { queueMembers.add (element); return element; } public Object dequeue() { return queueMembers.removeFirst(); }}
现在我们有了Queue
Java 1.5在java.util包中
Queue<String> myQueue = new LinkedList<String>();myQueue.add("Add Me");myQueue.add("Add Me Too");String enqueued = myQueue.remove();
你可以看到LinkedList类的add和remove方法被分别用于执行enqueuing和dequeuing操作。实际上没有更好的可用方法;Queue接口提供了新的offer和poll方法,如下显示(截取自SimpleQueueUsageExampleP
Queue<String> myQueue = new LinkedList<String>();boolean offerSuccess;// offer method tries to enqueue. // A boolean is returned telling you if the // attempt to add to the queue was successful or notofferSuccess=myQueue.offer("Add Me");offerSuccess=myQueue.offer("Add Me Too");// peek at the head of the queue, but don't grab itObject pull = myQueue.peek();String enqueued = null;// grab the head of the queueif (pull!=null) enqueued = myQueue.poll();System.out.println(enqueued);
如果你的Queue有固定长度限制(常常是这种情形),使用add方法向Queue中添加内容,将导致抛出一个非检查异常。当你编译SimpleQueueUsageExample的代码时,编译器将会就此问题发出警告。相反,当新的offer方法试图添加时,如果一切正常则会返回TRUE,否则,返回FALSE。
你也可以使用poll方法去从Queue中提取元素。如果在Queue中没有元素存在,poll方法将会返回一个null(即不会抛出异常)。在某些情况下,你可能不想提取头部的元素而只是想看一下。Queue接口提供了一个peek方法来做这样的事情。如果你正在处理一个空Queue,peek方法返回null。象add-offer和remove-poll关系一样,还有peek-element关系。在Queue为空的情形下,element方法抛出一个非检查异常。但如果在Queue中有元素,peek方法允许你查看一下第一个元素而无需真的把他从Queue中取出来。peek方法的用法在SimpleQueueUsageExampleP
AbstractQueue类
如你所见,java.util.LinkedList类实现了java.util.Queue接口,同样,AbstractQueue也是这样。
PriorityQueue类
在PriorityQueue中,当你添加元素到Queue中时,实现了自动排序。根据你使用的PriorityQueue的不同构造器,Queue元素的顺序要么基于他们的自然顺序要么通过PriorirtyQueue构造器传入的Comparator来确定。下面的代码示例了PirorityQueue类的使用方法。在Queue的前边是字符串"Alabama"-由于元素在PriorityQueue中是按自然顺序排列的(此例中是按字母表顺序)。
PriorityQueue<String> priorityQueue = new PriorityQueue<String>();priorityQueue.offer("Texas");priorityQueue.offer("Alabama");priorityQueue.offer("California");priorityQueue.offer("Rhode Island");int queueSize = priorityQueue.size();for (int i =0; i< queueSize; i++){ System.out.println(priorityQueue.poll());}
执行结果如下:
AlabamaCaliforniaRhode IslandTexas
Queue各项按照自然顺序-字母顺序-来排列
如上提到的,你可以创建你自己的Comparator类并提供给PirorityQueue。如此,你可以定义你自己的排序方式。在PriorityQueueComparatorU
private String name;private int population;public State(String name, int population){ super(); this.name = name; this.population = population;} public String getName(){ return this.name;}public int getPopulation(){ return this.population;}public String toString(){ return getName() + " - " + getPopulation();}
在PriorityQueueComparatorU
PriorityQueue<State> priorityQueue = new PriorityQueue(6, new Comparator<State>(){ public int compare(State a, State b) { System.out.println("Comparing Populations"); int populationA = a.getPopulation(); int populationB = b.getPopulation(); if (populationB>populationA) return 1; else if (populationB<populationA) return -1; else return 0; }});
执行PriorityQueueComparatorU
阻塞Queue
Queue通常限定于给定大小。迄今为止,通过Queue的实现你已经看到,使用offer或add方法enqueueQueue(并用remove或poll来dequeueQueue)都是假设如果Queue不能提供添加或移除操作,那么你不需要等待程序执行。java.util.concurrent.BlockingQueue接口实现阻塞。它添加了put和take方法。举一个例子可能更有用。
使用原来的producer/consumer关系来假定你的producer写一个Queue(更特定是一个BlockingQueue)。你有一些consumer正从Queue中读取,在一个有序的方式下,哪种方式是你希望看到的。基本上,每个consumer需要等待先于它并获准从Queue中提取项目的前一个consumer。用程序构建此结构,先生成一个producer线程用于向一个Queue中写数据,然后生成一些consumer线程从同一Queue中读取数据。注意,线程会阻塞另一线程直到当前线程做完从Queue中提取一项的操作。
下面的代码展示了类Producer写BlockingQueue的过程。注意run方法中的对象(你有责任实现,因为你继承了Thread)在等待了随机数量的时间(范围从100到500毫秒)后,被放进了BlockingQueue。放到Queue中的对象只是一些包含消息产生时的时间的字符串。
添加对象的实际工作是由如下语句实现的:
blockingQueue.put("Enqueued at: " + time)
put方法会抛出InterruptedException,因此,put操作需要被try...catch块包围,用来捕获被抛出的异常
从producer中提取消息的是Consumer对象,它也继承自Thread对象并因此要实现run方法(见Listing2
Consumer类在设计上是类似于Producer类的。Consumer类使用take方法去从Queue中取出(即dequeue)消息,而不是将消息放到BlockingQueue中。如前所述,这需要等待到有什么内容确实存在于Queue中时才发生。如果producer线程停止放置(即enqueue)对象到Queue中,那么consumer将等待到Queue的项目有效为止。下面所示的TestBlockingQueue类,产生四个consumer线程,它们从BlockingQueue中尝试提取对象。
import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;public class TestBlockingQueue{ public static void main(String args[]) { BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>(); Producer producer = new Producer(blockingQueue, System.out); Consumer consumerA = new Consumer("ConsumerA", blockingQueue, System.out); Consumer consumerB = new Consumer("ConsumerB", blockingQueue, System.out); Consumer consumerC = new Consumer("ConsumerC", blockingQueue, System.out); Consumer consumerD = new Consumer("ConsumerD", blockingQueue, System.out); producer.start(); consumerA.start(); consumerB.start(); consumerC.start(); consumerD.start(); }}
Figure 1.
下面一行创建BlockingQueue:
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
注意,它使用BlockingQueue的LinkedBlockingQueue实现。这是因为BlockingQueue是一个抽象类,你不能直接实例化它。你也可以使用ArrayBlockingQueueQueue类型。ArrayBlockingQueue使用一个数组作为它的存储设备,而LinkedBlockingQueue使用一个LinkedList。ArrayBlockingQueue的容量是固定的。对于LinkedBlockingQueue,最大值可以指定;默认是无边界的。本示例代码采用无边界方式。
在类的执行期间,从Queue中读取对象以顺序方式执行(见下面例子的执行)。实际上,一个consumer线程阻塞其他访问BlockingQueue的线程直到它可以从Queue中取出一个对象。
DelayQueue-我是/不是不完整的
在某些情况下,存放在Queue中的对象,在它们准备被取出之前,会需要被放在另一Queue中一段时间。这时你可使用java.util.concurrent.DelayQueue类,他实现类BlockingQueue接口。DelayQueue需要Queue对象被驻留在Queue上一段指定时间。
我想用来证实它的现实例子(这可能是你非常渴望的)是关于松饼(muffins)。噢,Muffin对象(象我们正在谈论的Java-没有coffee双关意图)。假定你有一个DelayQueue并在其中放了一些Muffin对象。Muffin对象(如下所示)必须实现java.util.concurrent.Delayed接口,以便可被放在DelayQueue中。这个接口需要Muffin对象实现getDelay方法(如下所示)。getDelay方法,实际上声明给多长时间让对象保存在DelayQueue中。当该方法返回的值变为0或小于0时,对象就准备完毕(或在本例子中,是烤制完毕)并允许被取出(见
Muffin类也实现compareTo(java.util.concurrent.Delayed)方法。由于Delayed接口继承自
由于你不是真想去吃没有完全烤熟的Muffin,因此,需要将Muffin放在DelayQueue中存放推荐的烤制时间。Listing4,取自DelayQueueUsageExample类,展示了从DelayQueue中enqueue和dequeueMuffin对象。
如你所见,对Muffin对象的烤制时间是使用它的构造器设置的(构造器期望烤制时间是以秒计)。
如前所讲,Muffin对象放到DelayQueue中是不允许被取出的,直到他的延时时间(又叫烤制时间)超期。元素被从Queue中取出基于最早的延时时间。在本例中,如果你有一些已经烤过的Muffin对象,他们将按他们已经等待多久而被取出(换句话说,最早被烤制的Muffin会在新烤制的Muffin之前被取出)。
SynchronousQueue
在Java1.5中,另外一种阻塞Queue实现是SynchronousQueue。相当有趣的是,该Queue没有内在容量。这是故意的,因为Queue意在用于传递目的。这意味着,在一个同步Queue结构中,put请求必须等待来自另一线程的给SynchronousQueue的take请求。同时,一个take请求必须等待一个来自另一线程的给SynchronousQueue的put请求。用程序来示例此概念,可参见示例代码。类似于前边的LinkedBlockingQueue例子,它包含一个consumer(SynchConsumer),见Listing5
Listing5
在Listing6
TestSynchQueue
import java.util.concurrent.SynchronousQueue;import java.util.concurrent.LinkedBlockingQueue;public class TestSynchQueue{ public static void main(String args[]) { SynchronousQueue<String> synchQueue = new SynchronousQueue<String>(); SynchProducer producer = new SynchProducer("ProducerA",synchQueue, System.out); SynchConsumer consumerA = new SynchConsumer("ConsumerA", synchQueue, System.out); consumerA.start(); producer.start(); }}
当试图明白隐藏在SynchronousQueue后面的概念时,要牢记这些Queue通常被使用在什么地方。JavaDoc中关于同步Queue指出:
"它们[同步Queue]是适合于传递设计,在那里运行在一个线程中的对象必须与运行在另外一个线程中的对象同步以便于交给它一些信息,时间或任务。"
- Queue-java
- Java: Queue
- Java: Queue
- Queue [Java]
- Queue Java
- java Queue
- 【java】Queue
- Java - Queue Stack
- java Queue的使用
- java Queue 的用法
- 解读java.util.queue
- Java Queue Example
- 学习Java: Queue
- Java文件Queue实现
- java Queue 的用法
- java 队列Queue
- Java Queue队列
- java Queue 的用法
- Android PNG渐变背景图片失真问题
- Use layout_height of 0dip instead of wrap_content for better Performance
- UncaughtExceptionHandler
- Handler执行时机
- java Volatile
- Queue [Java]
- UVA 12012 - Detection of Extraterrestrial(KMP)
- 解析iphone多线程
- Cocoa程序支持多国语言环境
- Xib文件创建UITableViewCell
- FAT32学习笔记(二)——spec
- iphone开发中使用动态库(dylib)和…
- Hibernate重点知识笔记-必看
- 理解iphone开发中的delegate(委托)