队列基本了解

什么是队列

队列(Queue),设计用于在处理之前保存元素的集合。除了基本的Collection操作之外,队列还提供了额外的插入、提取和检查操作。这些方法中的每一个都以两种形式存在:一种在操作失败时抛出异常,另一种返回特殊值( null或false ,具体取决于操作)。

方法函数

抛出异常返回false或null
插入boolean add(e)boolean offer(e)
消除E remove()E poll()
检查E element()E peek()
  • add(e) / offer(e):在队尾添加元素,当队列存储满了,add(e)会抛出异常,offer(e)返回false。
  • remove() / poll():移除队尾的元素并返回,当队列为空时,remove()抛出异常,poll()返回null。
  • element() / peek():获取队尾的元素,当队列为空时,element()抛出异常,peek()返回null。

当队列可以存储特殊值,例如null。因为poll()和peek()在队列空时会返回null,造成无法确定队列是否还有元素,这个时候最好用可以抛出异常的方法来区分。

普通队列

LinkedList

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{

}

public interface Queue<E> extends Collection<E> {}

LinkedList实现了Deque双端队列,而双端队列继承了Queue,所以LinkedList不仅有集合的特性,也有队列的特性。

public static void main(String[] args) {
    Queue<String> queue = new LinkedList<>();
    
    queue.add("A");
    String str = queue.element(); 
    System.out.println(str); // A
    System.out.println(queue); // [A]
    
    queue.remove();
    System.out.println(queue); // []
    
    String str2 = queue.element(); // java.util.NoSuchElementException
}

当队列里面没有数据的时候,element()会抛出异常。

public static void main(String[] args) {
    Queue<String> queue = new LinkedList<>();
    
    queue.offer("A");
    String str = queue.peek();
    System.out.println(str); // A
    System.out.println(queue); // [A]
    
    queue.poll(); 
    System.out.println(queue); // []
    
    String str2 = queue.peek(); // null
    System.out.println(str2);
}

当队列里面没有数据的时候,peek()不会抛出异常,直接返回null。

PriorityQueue

优先级队列

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {
    
    /**
     * 默认初始容量
     */
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    
    /**
     * 队列存储数组
     * 优先级队列表示为平衡二叉堆:queue[n] 的两个孩子是 queue[2n+1] 和 queue[2(n+1)]。
     * 优先级队列按比较器排序,如果比较器为空,则按元素的自然顺序排序:
     * 对于堆中的每个节点 n 和 n 的每个后代 d,n <= d。
     * 具有最低值的元素在 queue[0] 中,假设队列是非空的。
     */
    transient Object[] queue;
    
    /**
     * 队列的大小
     */
    private int size = 0;
    
    /**
     *  比较器
     */
    private final Comparator<? super E> comparator;
    
    /**
     * 无参构造器,调用两个参数的构造器
     */
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
    
    /**
     * 指定初始容量构造器,调用两个参数的构造器
     */
    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }
    
    /**
     * 指定比较器的构造器,调用两个参数的构造器
     */
    public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }
    
    /**
     *  指定初始容量和比较器的构造器
     */
    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
    
    ...}

添加元素

public boolean add(E e) {
    return offer(e);
}

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);
    return true;
}
  • 当队列达到当前存储的最大容量,调用grow(i+1)方法进行扩容
private void grow(int minCapacity) {
    int oldCapacity = queue.length;
    // Double size if small; else grow by 50%
    int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                     (oldCapacity + 2) :
                                     (oldCapacity >> 1));
    // overflow-conscious code
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    queue = Arrays.copyOf(queue, newCapacity);
}

判断是否oldCapacity < 64,结果为true 则扩容oldCapacity + (oldCapacity + 2),即2*(oldCapacity +1) 加倍扩容。结果为false 则扩容oldCapacity + (oldCapacity >> 1),即oldCapacity + 0.5*oldCapacity,扩容50%。

  • suftUp()判断是否有比较器调用不同的方法
private void siftUp(int k, E x) {
    if (comparator != null)
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (key.compareTo((E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = key;
}

@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

当优先级队列没有指定比较器的时候,默认使用Comparable的compareTo方法进行比较。指定比较器Comparator则用比较器的compare方法进行比较。

例子

  • 未指定比较器
@Data
public class Gen {

    private int age;

    public Gen(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Queue<Integer> queue = new PriorityQueue<>();
        queue.add(100);
        queue.add(80);
        queue.add(90);
        System.out.println(queue); // [80, 100, 90]
        System.out.println(queue.peek()); // 80
    }

}

new PriorityQueue<>()未指定比较器,默认调用siftUpComparable(int k, E x)方法。

@Data
public class Gen {

    private int age;

    public Gen(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Queue<Gen> queue = new PriorityQueue<>();
        Gen gen1 = new Gen(100);
        Gen gen2 = new Gen(60);
        Gen gen3 = new Gen(90);
        /*
         *  java.lang.ClassCastException: 
         *  com.xx.xx.Gen cannot be cast to java.lang.Comparable
         */
        queue.add(gen1); 
        queue.add(gen2);
        queue.add(gen3);
        System.out.println(queue);
        System.out.println(queue.peek());
    }
}

ClassCastException类转换异常,因为siftUpComparable(_int _k, E x)方法中进行了强转Comparable<? _super _E> key = (Comparable<? _super _E>) x,类Gen没有实现Comparable接口。

@Data
public class Gen implements Comparable<Gen>{

    private int age;

    public Gen(int age) {
        this.age = age;
    }
    
    @Override
    public int compareTo(Gen o) {
        return this.getAge() - o.getAge();
    }

    public static void main(String[] args) {
        Queue<Gen> queue = new PriorityQueue<>();
        Gen gen1 = new Gen(100);
        Gen gen2 = new Gen(60);
        Gen gen3 = new Gen(90);
        queue.add(gen1);
        queue.add(gen2);
        queue.add(gen3);
        System.out.println(queue); // [Gen(age=60), Gen(age=100), Gen(age=90)]
        System.out.println(queue.peek()); // Gen(age=60)
    } 
}

实现Comparable接口并重写compareTo方法后可以正常执行。‘

  • 指定比较器
/**
 * 自定义比较器
 */
public class GenComparator implements Comparator<Gen> {

    @Override
    public int compare(Gen o1, Gen o2) {
        return o1.getAge() - o2.getAge();
    }
}

@Data
public class Gen {

    private int age;

    public Gen(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Queue<Gen> queue = new PriorityQueue<>(new GenComparator());
        Gen gen1 = new Gen(100);
        Gen gen2 = new Gen(60);
        Gen gen3 = new Gen(90);
        queue.add(gen1);
        queue.add(gen2);
        queue.add(gen3);
        System.out.println(queue); // [Gen(age=60), Gen(age=100), Gen(age=90)]
        System.out.println(queue.peek()); // Gen(age=60)
    }
}

自定义GenComparator实现Comparator接口。添加元素的时候调用siftUpUsingComparator(_int _k, E x)接口,使用自定义比较器进行比较。

阻塞队列

支持在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用的操作。

BlockingQueue

抛出异常返回特殊值阻塞定时
插入add(e)offer(e)put(e)offer(e, time, unit)
移除remove()poll()take()poll(time, unit)
检查element()peek()
public interface BlockingQueue<E> extends Queue<E> {}

BlockingQueue接口继承Queue,所以有Queue的方法,并且拥有特有的阻塞方法和定时方法。

ArrayBlockingQueue

由数组支持的有界阻塞队列。此队列对元素进行 FIFO(先进先出)排序。队列的头部是在队列中时间最长的元素。队列的尾部是在队列中时间最短的元素。新元素被插入到队列的尾部,队列检索操作获取队列头部的元素。
这是一个经典的“有界缓冲区”,其中一个固定大小的数组保存由生产者插入并由消费者提取的元素。一旦创建,容量将无法更改。尝试put元素放入完整队列将导致操作阻塞;尝试从空队列中take元素同样会阻塞。

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
        
   /**
    * 创建具有给定(固定)容量和指定访问策略的ArrayBlockingQueue 
    */
   public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
   }
   
   /**
    * 创建具有给定(固定)容量和指定访问策略的ArrayBlockingQueue 
    * fair: 指定是否公平锁,如果为true ,则在插入或删除时阻塞的线程的队列访问将按 FIFO 
    *       顺序处理;如果为false ,则未指定访问顺序。
    */
   public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
   }
  
   /**
    * 创建具有给定(固定)容量、指定访问策略并最初包含给定集合的元素的ArrayBlockingQueue 
    * ,按集合迭代器的遍历顺序添加。
    */
   public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }
}

LinkedBlockingQueue

基于链接节点的可选有界阻塞队列。此队列对元素进行 FIFO(先进先出)排序。队列的头部是在队列中时间最长的元素。队列的尾部是在队列中时间最短的元素。新元素被插入到队列的尾部,队列检索操作获取队列头部的元素。链接队列通常比基于数组的队列具有更高的吞吐量,但在大多数并发应用程序中性能更不可预测。
可选的容量绑定构造函数参数用作防止过度队列扩展的一种方式。容量(如果未指定)等于Integer.MAX_VALUE 。链接节点在每次插入时动态创建,除非这会使队列超出容量。

PriorityBlockingQueue

使用与类PriorityQueue相同的排序规则并提供阻塞检索操作的无界阻塞队列。虽然此队列在逻辑上是无界的,但尝试添加可能会由于资源耗尽而失败(导致OutOfMemoryError )。此类不允许null元素。依赖于自然排序的优先级队列也不允许插入不可比较的对象(这样做会导致ClassCastException )。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值