您的位置:首页 > 编程语言 > Java开发

java队列(一)

2016-07-11 01:05 495 查看
简介:队列是FIFO先进先出模式的容器,容器两端的一端放入,另一端取出。我们可以通过队列将封装好的对象从应用程序的一个区域传到另一个区域。队列大量应用在并发编程中。jdk中队列的两种实现为LinkedList和PriorityQueue。

Queue接口

public interface Queue<E> extends Collection<E> {
boolean offer(E var1);

E poll();

E remove();

E peek();

E element();
}


LinkedList

下面结合LinkedList实现,通过例子对接口中的方法进行讲解:

public class QueueDemo {

/**
* 打印队列
* @param queue
*/
public static void printQ(Queue queue) {
//peek方法是取出队头的对象,如果队头为空返回null,注意同样操作的element方法在队头为空的时候返回NoSuchElementException异常
while(queue.peek() != null)
//remove方法取出队头的对象返回,并将该对象移出队列,如果队头为空,返回NoSuchElementException异常,注意注意同样操作的poll方法在队头为空的时候返回null
System.out.print(queue.remove() + " ");
System.out.println();
}
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
Random rand = new Random(47);
for(int i = 0; i < 10; i++)
//offer方法将一个element插入到队尾,添加失败则返回false
queue.offer(rand.nextInt(i + 10));

printQ(queue);
Queue<Character> qc = new LinkedList<Character>();
for(char c : "Brontosaurus".toCharArray())
//offer方法将一个element插入到队尾,添加失败则返回false
qc.offer(c);
printQ(qc);
}
}


Output:

8 1 1 1 5 14 3 1 0 1

B r o n t o s a u r u s

总结:offer插入对象到队尾,poll(poll修剪,有移除的意思)和remove(remove表示移除)取出队头的时删除队头的对象,但是在没有元素的时候返回类型不同,peek(peek偷看一眼,没有删除的意思)和element(元素也没有移除的意思)取出队头对象不删除,同样没元素的时候返回类型不同。poll和peek元素为空时返回类型均为null,而remove和element在元素为null时返回均为NoSuchElementException。

PriorityQueue

优先级队列可以根据默认的或自定义(提供自己的Comparator)的优先级来对队列中的元素进行“排列”,PriorityQueue保证了当我们调用peek,poll,remove,element方法时,得到的都是队列中优先级最高的元素。

例子如下:

public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
Random rand = new Random(47);
for (int i = 0; i < 10; i++)
priorityQueue.offer(rand.nextInt(i + 10));
// 默认的实现是按照自然顺序(natural order)进行排序的,对于Integer类型就是从小到大
QueueDemo.printQ(priorityQueue);//1

List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
priorityQueue = new PriorityQueue<Integer>(ints);
// 默认的实现是按照自然顺序(natural order)进行排序的,对于Integer类型就是从小到大
QueueDemo.printQ(priorityQueue);//2

// 通过在构造函数中提供自己的Comparator来修改排序规则,比如这里使用集合工具类的reverseOrder对Integer进行逆序排列
priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder());
priorityQueue.addAll(ints);
QueueDemo.printQ(priorityQueue);//3;

String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
//Arrays提供的工具类按照""对String分割转化成String类形的list列表
//注意这里List是允许重复元素的
List<String> strings = Arrays.asList(fact.split(""));
PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);
//前面会输出3个空格字符,然后输出A,B等
QueueDemo.printQ(stringPQ);//4
// 通过在构造函数中提供自己的Comparator来修改排序规则,比如这里使用集合工具类的reverseOrder对String进行逆序排列
stringPQ = new PriorityQueue<String>(strings.size(), Collections.reverseOrder());
stringPQ.addAll(strings);
QueueDemo.printQ(stringPQ);//5
//注意这里Set集合中的是排异的,即没有两个相同的对象
Set<Character> charSet = new HashSet<Character>();
char[] charArray = fact.toCharArray();
for (char c : fact.toCharArray())
charSet.add(c); // Autoboxing
PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(charSet);
QueueDemo.printQ(characterPQ);
}
}


Output:

0 1 1 1 1 1 3 5 8 14

1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25

25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1

空 空 空 A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W

W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A

空 A B C D E F H I L N O S T U W

空表示空格

几种常见的并发实现

主要有

PriorityBlockingQueue

ArrayBlockingQueue

LinkedBlockingQueue

ConcurrentLinkedQueue

例子如下:

public class QueueBehavior {
private static int count = 10;

/**
*
* @param queue
* @param gen
*/
static <T> void test(Queue<T> queue, Generator<T> gen) {
for (int i = 0; i < count; i++) {
//String s = (String) gen.next();
queue.offer(gen.next());// 不断存入next返回的数据到队尾
}
while (queue.peek() != null)// 查看队头的数据,如果没有,则返回null,element则会报NoSuchElementException
System.out.print(queue.remove() + " ");//
System.out.println();
}

static class Gen implements Generator<String> {
// 依据“ ”进行分割,分割成String[]数组
String[] s = ("one two three four five six seven " + "eight nine ten").split(" ");
// 初始化为0
int i;

// 复写接口中的方法,产生下一个String对象
@Override
public String next() {
return s[i++];// 先返回s[i],再i++,这样当i到9的时候,return
// s[9],然后i=10,test方法中的for条件判断不满足,退出
// 如果++i的话,i=8的时候,return
// s[9],for条件中i=9,满足,再到next方法中,先i+1变为10,这时return s[10]已经越界,报错
}
}

public static void main(String[] args) {

test(new LinkedList<String>(), new Gen());
test(new PriorityQueue<String>(), new Gen());
// ArrayBlockingQueue拥有固定尺寸,在它阻塞之前,你可以向其中放入有限的元素
test(new ArrayBlockingQueue<String>(count), new Gen());
// LinkedBlockingQueue是无界的队列
test(new LinkedBlockingQueue<String>(), new Gen());
// 以上两个都是BlockingQueue的实现

// ConcurrentLinkedQueue是免锁容器,允许并发的读取和写入(只有部分是被拷贝和修改的copied and
// modified),在修改完成前,读取者是看不到这些元素的
test(new ConcurrentLinkedQueue<String>(), new Gen());
//提供了可阻塞读取功能的优先级队列,当队列中没有元素的时候,会阻塞读取者,这种队列不需要额外的同步操作
test(new PriorityBlockingQueue<String>(), new Gen());
}
}


Output:

one two three four five six seven eight nine ten

eight five four nine one seven six ten three two

one two three four five six seven eight nine ten

one two three four five six seven eight nine ten

one two three four five six seven eight nine ten

eight five four nine one seven six ten three two

自定义优先级规则的PriorityQueue优先级队列

在以下的例子中,我们通过重写实现了Cmparable的ToDoItem类中的compareTo方法定制了队列中元素的排序规则,PriorityQueue在存取队列中的元素的时候会根据排序规则来对队列中的元素重新排列。

package inner;

import java.util.*;

class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
// 嵌套类
static class ToDoItem implements Comparable<ToDoItem> {
private char primary;
private int secondary;
private String item;

public ToDoItem(String td, char pri, int sec) {
primary = pri;
secondary = sec;
item = td;
}

// 覆写Comparable的compareTomcat方法,提供优先级排列的方式
// 返回负整数表示<,返回正整数表示>,返回0表示等于=
// Compares this object with the specified object for order
// arg就是当前对象(后一个加入的ToDoItem)需要与之比较的对象(前一个加入的ToDoItem,被比较对象)
public int compareTo(ToDoItem arg) {
// 比较规则:
// 先比较第一优先级
if (primary > arg.primary)// 比待比较(先加入)的优先级高
return +1;
if (primary == arg.primary)
if (secondary > arg.secondary)// 第一优先级相同,第二优先级比待比较(先加入)的优先级高
return +1;
else if (secondary == arg.secondary)// 第二优先级也相同,说明两个对象的优先级相同
return 0;
return -1;// 其余情况表明前一个加入的被比较对象优先级高
}

// 重载toString方法,返回用String形式的(String represention)ToDoItem对象
// 为了显示出char的字符表现形式,需要将char通过Character.toString转化
@Override
public String toString() {
return Character.toString(primary) + secondary + ": " + item;
}
}

public void add(String td, char pri, int sec) {
// 调用PriorityQueue的add方法,永远return true,传入为null会包空指针异常
super.add(new ToDoItem(td, pri, sec));
}

public static void main(String[] args) {
ToDoList toDoList = new ToDoList();
// 第二个参数字母A,B,C代表第一优先级,等级依次降低,第三个参数数字是第二优先级,数字越高优先级越低
toDoList.add("Empty trash", 'C', 4);
toDoList.add("Feed dog", 'A', 2);
toDoList.add("Feed bird", 'B', 7);
toDoList.add("Mow lawn", 'C', 3);
toDoList.add("Water lawn", 'A', 1);
toDoList.add("Feed cat", 'B', 1);
while (!toDoList.isEmpty())
// 这里会调用ToDoItem中重载的toString方法打印ToDoItem对象(以String的形式打印出来)
System.out.println(toDoList.remove());
}
}


结果:

A1: Water lawn

A2: Feed dog

B1: Feed cat

B7: Feed bird

C3: Mow lawn

C4: Empty trash

疑问:在toDoList.add(“Feed cat”, ‘B’, 1);这段代码执行完之后,toDoList队列中的元素如下,可以看到并不完全是按照优先级顺序排列的:

[A1: Water lawn, A2: Feed dog, B1: Feed cat, C4: Empty trash, C3: Mow lawn, B7: Feed bird]

但是通过remove操作(内部调用Queue接口的poll方法),已经是将队头的元素一个个按照优先级顺序取出返回打印,目前还不知道具体的原因,猜想是PriorityQueue队列的实现导致的。如有高手知道原因,也可以留言告知下,不胜感激~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java