【Java基础】Queue基础

一.Java集合架构图

点击放大查看
在这里插入图片描述

二.什么是Queue?

  • 队列(Queue)是一种经常使用的集合。Queue实际上是实现了一个先进先出(FIFO:First In First Out)的有序列表(有序集合)

  • 队列List的区别在于,List可以在任意位置添加和删除元素

    • Queue只有两个操作:

      • 把元素添加到队列末尾;
      • 从队列头部取出元素。

例如:超市的收银台就是一个队列:
在这里插入图片描述
排到前面的先买单,排到后面的后买单


三.Java中Queue提供的标准方法

Java的标准库中,队列接口Queue定义了以下几个方法:

方法 方法描述
int size() 获取队列长度
boolean add(E) / boolean offer(E) 添加元素到队尾
E remove() / E poll() 获取队首元素并从队列中删除
E element() / E peek() 获取队首元素但并不从队列中删除
  • 对于具体的实现类,有的Queue有最大队列长度限制,有的Queue没有
  • 注意到添加删除获取队列元素总是有两个方法, 这是因为在添加或获取元素失败时,这两个方法的行为是不同的。 我们用一个表格总结如下
throw Exception 返回false或null
添加元素到队尾 add(E e) boolean offer(E e)
取队首元素并删除 E remove() E poll()
取队首元素但不删除(获取第一个元素) E element() E peek()
  • 举个栗子,假设我们有一个队列

  • 当调用add()方法添加失败时(可能超过了队列的容量)会抛出异常

  • 当调用offer()方法当添加失败时,不会抛异常,而是返回false

  • 当调用remove()取出队首元素时,如果是一个空队列,调用remove()方法,它会抛出异常

  • 当调用poll()方法来取出队首元素当获取失败时,它不会抛异常,而是返回null

  • 当调用poll(),会获取队首元素,并且获取到的元素已经从队列中被删除

  • 当调用peek(),会获取队首元素时,并不会从队列中删除这个元素,所以可以反复获取的都是第一个元素

注意:不要把null添加到队列中,否则poll()方法返回null时,很难确定是取到了null元素还是队列为空。

四.Queue小结

队列Queue实现了一个先进先出(FIFO)的数据结构:

  • 通过add()/offer()方法将元素添加到队尾;
  • 通过remove()/poll()从队首获取元素并删除;
  • 通过element()/peek()从队首获取元素但不删除。
  • 要避免把null添加到队列

五.优先队列PriorityQueue

1.什么是优先队列

Queue是一个先进先出(FIFO)的队列。

  • 在银行柜台办业务时,我们假设只有一个柜台在办理业务,但是办理业务的人很多,怎么办?

    • 可以每个人先取一个号,如:A1、A2、A3……然后,按照号码顺序依次办理,实际上这就是一个Queue
    • 如果这时来了一个VIP客户,他的号码是V1,虽然当前排队的是A10、A11、A12……但是柜台下一个呼叫的客户号码却是V1
    • 这个时候,我们发现,要实现“VIP插队”的业务,用Queue就不行了,因为Queue会严格按FIFO的原则取出队首元素。 我们需要的是优先队列PriorityQueue。

PriorityQueue和Queue的区别在于 :它的 出队顺序元素的优先级有关,对PriorityQueue调用remove()或poll()方法,返回的总是优先级最高的元素。

  • 要使用PriorityQueue,我们就必须给每个元素定义“优先级”

2. Java中使用优先队列

  • 放入PriorityQueue的对象,必须实现比较器Comparator接口,PriorityQueue会根据元素的排序顺序决定出队的优先级
public class Main {
    public static void main(String[] args) {
        Queue<String> queue = new PriorityQueue<>();
        // 添加3个元素到队列:
        queue.offer("apple");
        queue.offer("pear");
        queue.offer("banana");
        System.out.println(queue.poll()); // apple
        System.out.println(queue.poll()); // banana
        System.out.println(queue.poll()); // pear
        System.out.println(queue.poll()); // null,因为队列为空
    }
}

  • 上面代码存入的顺序是"apple"、"pear"、"banana",取出的顺序却是"apple"、"banana"、"pear", 这是因为从字符串的排序看,"apple"排在最前面,"pear"排在最后面。
  • 说明PriorityQueue默认是根据Comparator比较器进行自然排序

如果存放的对象没有使用Comparable接口

  • PriorityQueue允许我们提供一个Comparator对象判断两个元素的顺序

以银行排队业务为例,实现一个PriorityQueue:

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
/**
 * @Description TODO
 * @Author JianPeng OuYang
 * @Date 2020/2/1 15:40
 * @Version v1.0
 */
public class TestPriorityQueue {
    public static void main(String[] args) {
        Queue<User> queue = new PriorityQueue<>(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                //如果首字符相等,按自然顺序排序
                if (o1.number.charAt(0) == o2.number.charAt(0)) {
                    // 如果两人的号都是A开头或者都是V开头,比较号的大小:
                    return o1.number.compareTo(o2.number);
                }
                //如果比较对象首字母不相等,且首字母为 “V”,保持顺序
                if (o1.number.charAt(0) == 'V') {
                    // u1的号码是V开头,优先级高:
                    return -1;

                    //如果比较对象首字母不相等,且首字母不为 “V”,交换顺序
                } else {
                    return 1;
                }
            }
        });
        // 添加3个元素到队列:
        queue.offer(new User("Bob", "A2"));
        queue.offer(new User("Alice", "A10"));
        queue.offer(new User("Alice", "A1"));
        queue.offer(new User("Boss", "V1"));
        System.out.println(queue.poll()); // Boss/V1
        System.out.println(queue.poll()); // Bob/A1
        System.out.println(queue.poll()); // Alice/A2
        System.out.println(queue.poll()); // null,因为队列为空
    }
}


class User {
    public  String name;
    public  String number;

    public User(String name, String number) {
        this.name = name;
        this.number = number;
    }

    @Override
    public String toString() {
        return name + "/" + number;
    }
}

3. ProrityQueue小结

  • PriorityQueue实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。

  • PriorityQueue默认按元素比较的自然顺序排序(必须实现Comparator接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。

六.双端队列Deque

1.什么是双端队列

  • Queue是队列,只能一头进,另一头出。
  • Deque 叫双端队列(Double Ended Queue),允许两头都进,两头都出

Java集合提供了接口Deque来实现一个双端队列,它的功能是:

  • 既可以添加到队尾,也可以添加到队首
  • 既可以从队首获取,又可以从队尾获取
  • Deque实际上也是继承自Queue,基于Queue进行拓展的
    public interface Deque<E> extends Queue<E> {}
    

2.对比Queue和Deque

Queue Deque
添加元素到队尾 add(E e) / offer(E e) addLast(E e) / offerLast(E e)
取队首元素并删除 E remove() / E poll() E removeFirst() / E pollFirst()
取队首元素但不删除 E element() / E peek() E getFirst() / E peekFirst()
添加元素到队首 addFirst(E e) / offerFirst(E e)
取队尾元素并删除 E removeLast() / E pollLast()
取队尾元素但不删除 E getLast() / E peekLast()
  • 对于添加元素到队尾的操作,Queue提供了add()/offer()方法,而Deque提供了addLast()/offerLast()方法。
    -添加元素到对首、取队尾元素的操作在Queue中不存在,在Deque中由addFirst()/removeLast()等方法提供。

  • Queue提供的add()/offer()方法在Deque中也可以使用,但使用Deque,最好不要调用offer(),而是调用offerLast()

3.Java中使用Deque

public class Main {
    public static void main(String[] args) {
        Deque<String> deque = new LinkedList<>();
        deque.offerLast("A"); // A  //添加到队尾
        deque.offerLast("B"); //A -> B  //添加到队尾
        deque.offerFirst("C"); // C -> A -> B //添加到队首

        System.out.println(deque.pollFirst()); // C, 剩下A -> B   //取队首元素并删除
        System.out.println(deque.pollLast()); // B   //取队尾元素并删除
        System.out.println(deque.pollFirst()); // A   //取队首元素并删除
        System.out.println(deque.pollFirst()); // null
    }
}

使用Deque,推荐总是明确调用offerLast()/offerFirst()或者pollFirst()/pollLast()方法。

Deque是一个接口,它的实现类有ArrayDeque和LinkedList


我们发现LinkedList真是一个全能选手,它即是List,又是Queue,还是Deque。但是我们在使用的时候,总是用特定的接口来引用它,这是因为持有接口说明代码的抽象层次更高,而且接口本身定义的方法代表了特定的用途。

// 不推荐的写法:
LinkedList<String> d1 = new LinkedList<>();
d1.offerLast("z");

// 推荐的写法:
Deque<String> d2 = new LinkedList<>();
d2.offerLast("z");

4.ArrayDeque

  • ArrayDeque是基于可变数组实现的,且 没有容量限制,可根据需求自动进行扩容的非线程安全的双端队列。该类性能可能快于stack,在作为队列时快于LinkedListArrayDeque作为stack的取代类,而使用。

    1. ArrayDeque 不允许空元素 null

    2. 当用作栈stack时,比 Stack 快,当用作队列Queue时,比 LinkedList 快
      一是因为它内部结构是一个循环数组(只需操作索引),二是没有使用 Synchronized 修饰方法(非线程安全)

    3. 扩容机制为:扩容成原来的两倍,然后将原来的内容复制到新数组中

    4. 容量限制:容量必须为2的幂次方,最小为8,默认为16.

    5. 应用场景:在很多场景下可以用来代替LinkedList,ArrayDeque 为双端队列,支持首部,尾部两端的操作,因此做双端操作可用于fifo等Queue, 做单端操作可做为Stack.

5.Deque总结

Deque实现了一个双端队列(Double Ended Queue),它可以:

  • 将元素添加到队尾或队首:addLast()/offerLast()/addFirst()/offerFirst()
  • 从队首/队尾获取元素并删除:removeFirst()/pollFirst()/removeLast()/pollLast()
  • 从队首/队尾获取元素但不删除:getFirst()/peekFirst()/getLast()/peekLast()
  • 总是调用xxxFirst()/xxxLast()以便与Queue的方法区分开;
    避免把null添加到队列。

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/104133335
今日推荐