Java 集合之队列

101 阅读7分钟

Queue

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队首。

和栈一样,队列也有两种实现方式,数组和链表

PriorityQueue

优先级队列,通过构造堆结构使最大(最小)元素先出队,使用数组进行存储;详解

public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
    // 默认容量11
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 队列元素,数组集合
    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) { 
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }  
}

入队

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;
} 
// 构造最小(大)堆,保证堆顶元素最小(大) 
private void siftUp(int k, E x) {
    if (comparator != null) // 不同的比较策略
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}
// 通过比较(利用堆的特性)把元素x 放到数组的指定位置
private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        // 无符号右移,相当于 除以2,目的: 通过构建最小堆,把最小值放到0位置
        int parent = (k - 1) >>> 1; // 父结点
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e; // x如果比位置k的元素小,后移
        k = parent;
    }
    // 构建最小堆,把x放入堆中,保证顶点元素最小 
    queue[k] = x;
}

扩容

// 扩容
private void grow(int minCapacity) {
    int oldCapacity = queue.length; 
    // < 64 扩容翻倍 + 2,否则扩容50%
    int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) :  (oldCapacity >> 1));
    // 检查是否溢出
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity); // 计算扩容需要的容量
    // 通过复制算法把原数据复制到新队列
    queue = Arrays.copyOf(queue, newCapacity);
}
// 计算扩容需要的容量
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?  Integer.MAX_VALUE :  MAX_ARRAY_SIZE;
}

出队

实际上就是把堆顶的元素移除,然后通过堆的特性重新选出堆顶

public E poll() {
    if (size == 0)
        return null;
    int s = --size;
    modCount++;
    E result = (E) queue[0]; // 堆顶元素
    E x = (E) queue[s];
    queue[s] = null;
    if (s != 0)
        siftDown(0, x); // 重新选出堆顶
    return result;
}
// 重新选出堆顶
private void siftDown(int k, E x) {
    if (comparator != null) // 不同的比较策略
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}
// 从0开始,比较两个子结点,重新选出堆顶
private void siftDownUsingComparator(int k, E x) {
    int half = size >>> 1;
    while (k < half) {
        int child = (k << 1) + 1; // 左子结点
        Object c = queue[child];
        int right = child + 1; // 右子结点
        if (right < size && comparator.compare((E) c, (E) queue[right]) > 0)
            c = queue[child = right];
        if (comparator.compare(x, (E) c) <= 0)
            break;
        // c和child是左右两个子结点中较小(大)的元素,移动到父结点
        queue[k] = c;
        k = child;  
    }
    queue[k] = x;
}

取队首元素

// 取队首元素 始终是数组第0个元素
public E peek() {
    return (size == 0) ? null : (E) queue[0];
}

Deque

Deque:双向队列,支持两端操作;支持先入先出和后入先出,可以同时试下栈和队列的操作;

// 双向队列接口
public interface Deque<E> extends Queue<E> {
    // 头尾新增元素 无返回
    void addFirst(E e);
    void addLast(E e);
    // 头尾元素入队 有返回
    boolean offerFirst(E e)
    boolean offerLast(E e);
    // 头尾删除元素
    E removeFirst();
    E removeLast();
    // 头尾元素出队
    E pollFirst(); 
    E pollLast();
    // 头尾获取元素
    E getFirst(); 
    E getLast();
    // 头尾查看元素
    E peekFirst(); 
    E peekLast();
}

ArrayDeque

基于数组结构实现的双向队列

// java数组实现队列接口
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable {
    transient Object[] elements;// 数组元素集合,循环队列
    transient int head; // head元素指针,指向数组中head的位置 
    transient int tail; // tail元素指针,指向数组中tail的位置 
 	// 最小初始容量
    private static final int MIN_INITIAL_CAPACITY = 8;
    // 默认构造方法,构造长度为16的数组
    public ArrayDeque() {
        elements = new Object[16];
    } 
    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    } 
    public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }
    // 初始化数组
    private void allocateElements(int numElements) {
        elements = new Object[calculateSize(numElements)];
    }
    // 计算容量 这段代码的逻辑是算出大于numElements的最接近的2的n次方且不小于8
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY; 
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++; 
            if (initialCapacity < 0)    
                initialCapacity >>>= 1; 
        }
        return initialCapacity;
    }
}

入队

// 队首新增元素
public void addFirst(E e) {
    if (e == null)
        throw new NullPointerException();
    // head - 1 左移一位     
    elements[head = (head - 1) & (elements.length - 1)] = e;
    if (head == tail) // head  tail相遇,说明数组已满
        doubleCapacity(); // 扩容
}
// 队尾新增元素
public void addLast(E e) {
    if (e == null)
        throw new NullPointerException();
    elements[tail] = e;
    // tail + 1 右移一位     
    if ( (tail = (tail + 1) & (elements.length - 1)) == head) // head  tail相遇,说明数组已满
        doubleCapacity();// 扩容
}

扩容

// 扩容
private void doubleCapacity() {
    assert head == tail;
    int p = head;
    int n = elements.length;
    int r = n - p; // 头指针到数组末尾的元素个数
    int newCapacity = n << 1; // 扩容1倍
    if (newCapacity < 0)
        throw new IllegalStateException("Sorry, deque too big");
    // 创建新数组
    Object[] a = new Object[newCapacity];
    // 复制 head到数组末尾的元素
    System.arraycopy(elements, p, a, 0, r);
    // 复制 0~head-1的元素
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0;
    tail = n;
} 

出队

// 从队列头出队
public E pollFirst() {
    int h = head;
    @SuppressWarnings("unchecked")
    // 取队列头元素
    E result = (E) elements[h];
    // 队列头为空
    if (result == null)
        return null;
    elements[h] = null;     // 出队,把队头赋值为空
    // 队列头指针右移一位
    head = (h + 1) & (elements.length - 1);
    return result;
} 
// 从队列尾出队
public E pollLast() {
	// 队列尾指针左移一位
    int t = (tail - 1) & (elements.length - 1);
    @SuppressWarnings("unchecked")
    E result = (E) elements[t];
    // 队列尾为空
    if (result == null)
        return null;
    elements[t] = null; // 出队,把队尾赋值为空
    tail = t;
    return result;
}	

取队首元素

// 取队首元素 head指针的位置
public E peekFirst() { 
    return (E) elements[head];
}
// 取队尾元素 tail指针左移一位的位置
@SuppressWarnings("unchecked")
public E peekLast() {
    return (E) elements[(tail - 1) & (elements.length - 1)];
}

入栈和出栈都在 队列头执行

// 入栈
public void push(E e) {
    addFirst(e);
} 
// 出栈
public E pop() {
    return removeFirst();
}

LinkedList

链表实现队列

// java链表实现队列
public class LinkedList<E> extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
    transient int size = 0;// 队列大小
    transient Node<E> first;// 头结点
    transient Node<E> last;// 尾结点 
    // 内部类,双向链表的Node结点
    private static class Node<E> {
        E item;
        Node<E> next; // 后驱指针
        Node<E> prev; // 前驱指针
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
    // 构造方法
    public LinkedList() {
    }
    // Collection 构造方法
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    } 
    // 插入新结点First
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f); // 根据元素e构建新结点
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }
    // 移除头结点
    private E unlinkFirst(Node<E> f) { 
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
}

入队

// 队首新增结点
public boolean offerFirst(E e) {
    addFirst(e);
    return true;
}
public void addFirst(E e) {
    linkFirst(e);
} 
private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);// 根据元素e构建新结点
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
} 
// 队尾新增结点 
public boolean offerLast(E e) {
    addLast(e);
    return true;
}
public void addLast(E e) {
    linkLast(e);
} 
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);// 根据元素e构建新结点
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

出队

// 从队列头出队 
public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next; // 把first指针指向first的next结点
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
} 
// 从队列尾出队 
public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    last = prev;  // 把last指针指向last的prev结点
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}

取队首元素

// 取队首元素 first指针  
public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
} 
// 取队尾元素 last指针 
public E peekLast() {
    final Node<E> l = last;
    return (l == null) ? null : l.item;
} 

入栈和出栈都在队列头执行

// 入栈
public void push(E e) {
    addFirst(e);
} 
// 出栈
public E pop() {
    return removeFirst();
}

参考