JavaSE Queue集合

JavaSE Queue集合


目录

Queue 集合是什么?有什么用?

​ Queue 集合用于模拟数据结构“队列”。

​ Queue 集合中有一个 PriorityQueue 实现类,用于模拟“优先队列”。

​ Queue 集合中还有一个 Deque 接口,用于模拟“双端队列”。

返回目录


Queue 集合怎么用?

1 Queue 集合的继承图(常用部分)

在这里插入图片描述

返回目录


2 Queue 接口
2-1 常用方法

Queue 接口是 Collection 接口的子接口,模拟了“队列”这个数据结构。

public interface Queue<E> extends Collection<E> {
    // 作为队列使用
    //将元素加入队尾
    boolean offer(E e);
    //删除并返回队头元素。如果队列为空,返回 null
    E poll();
    //获取队头元素,但不删除。如果队列为空,返回 null
    E peek();
}

返回目录


3 Deque 接口
3-1 常用方法

Deque 接口是 Queue 接口的子接口,模拟了“双端队列”这个数据结构。

public interface Deque<E> extends Queue<E> {
    ...
    //1 作为双端队列使用
    //在队头、队尾插入元素
    boolean offerFirst(E e);
    boolean offerLast(E e);
    //删除并返回队头、队尾元素。如果队列为空,返回 null
    E pollFirst();
    E pollLast();
    //返回队头、队尾元素,但不删除。如果队列为空,返回 null
    E peekFirst();
    E peekLast();
    
    //2 作为栈使用
    //入栈
    void push(E e);
    //出栈
    E pop();
    //查看栈顶元素
    E peek();
    ...
}

返回目录


4 ArrayDeque 类
4-1 介绍

(1)ArrayDeque 类实现了 Deque 接口,是一个基于循环数组实现的双端队列。

(2)ArrayDeque 不能添加 null。

(3)ArrayDeque 的容量总是 2^n。

返回目录

4-2 底层结构

ArrayDeque 底层是一个 Object[] 数组,是一个循环数组,维护队头索引 head,队尾索引 tail。

public class ArrayDeque<E> ...{
	...
    transient Object[] elements;
    transient int head;  
    transient int tail;
	...
}

返回目录

4-3 扩容机制

(1)使用无参构造器初始化一个 ArrayDeque 对象时,数组长度为 16。

(2)使用有参构造器初始化一个 ArrayDeque 对象时,自定义容量 numElements;如果 numElements < 8,则数组长度为 8;如果 numElements >= 8,则数组长度比 numElements 大(严格大),并且是最近的 2^n(16、32、64…)。

(3)当数组占满后,扩容为原来的 2 倍。

public class ArrayDeque<E> ...{
	...
    transient Object[] elements;
    transient int head;  
    transient int tail;
    private static final int MIN_INITIAL_CAPACITY = 8;
    
    //无参构造器,初始化一个长度为 16 的 Object[] 数组
    public ArrayDeque() {
        elements = new Object[16];
    }
    
    //有参构造器,传入自定义容量
    //如果 numElements < 8,则初始化数组容量为 8
    //如果 numElements >= 8,则初始化数组容量比 numElements 大(严格大),并且是 2^n
    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }
    
    private void allocateElements(int numElements) {
        elements = new Object[calculateSize(numElements)];
    }
    
    //如果 numElements < 8,则返回 8
    //如果 numElements >= 8,则返回一个比 numElements 大的数,并且这个数是 2^n
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // Find the best power of two to hold elements.
        // Tests "<=" because arrays aren't kept full.
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;

            if (initialCapacity < 0)   // Too many elements, must back off
                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
        }
        return initialCapacity;
    }
    
    // 添加元素
    // ArrayDeque 不能添加 null
    public void addLast(E e) {
        // 不能添加 null
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        // 因为 elements.length = 2^n, 所以 elements.length - 1 低位全是 1,高位全是 0;
        // 所以 tail & elements.length - 1,相当于 tail % elements.length
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
    
    // 当数组占满后,扩容为原来的 2 倍
    private void doubleCapacity() {
        assert head == tail;
        int p = head;
        int n = elements.length;
        int r = n - p; // number of elements to the right of p
        int newCapacity = n << 1;
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        Object[] a = new Object[newCapacity];
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        elements = a;
        head = 0;
        tail = n;
    }
	...
}

返回目录

4-4 常用方法
public class ArrayDeque<E> ...{
	// 1 构造器
    // 无参构造器,初始容量为 16
    public ArrayDeque() {...}
    // 有参构造器,自定义容量,最终容量大于设定值,并且是 2^n
    public ArrayDeque(int numElements) {...}
    // 复制集合元素
    public ArrayDeque(Collection<? extends E> c) {...}
    
    // 2 作为双端队列
    //在队头、队尾插入元素
    boolean offerFirst(E e);
    boolean offerLast(E e);
    //删除并返回队头、队尾元素。如果队列为空,返回 null
    E pollFirst();
    E pollLast();
    //返回队头、队尾元素,但不删除。如果队列为空,返回 null
    E peekFirst();
    E peekLast();
    
    // 3 作为栈
    //入栈
    void push(E e);
    //出栈
    E pop();
    //查看栈顶元素
    E peek();
}

返回目录


5 LinkedList 类
5-1 介绍

该类底层是双向链表。

该类实现了 List 接口,可以根据索引来随机访问集合中的元素(效率低)。

该类实现了 Deque 接口,可以被当成双端队列、栈来使用(不推荐)。

该类可以添加 null,元素可以重复。

该类线程不安全。

返回目录

4-2 底层结构

LinkedList 底层是双向链表。

维护着指向头节点的 first 变量,指向尾节点的 last 变量。

节点类型为 Node 类,是 LinkedList 的内部类。

public class LinkedList<E> ...{
    ...
    //指向头节点,初始为Null
    transient Node<E> first;
    //指向尾节点,初始为Null
    transient Node<E> last;
    
    //在链表的末尾添加元素
	public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    //在链表的末尾添加元素
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    
    // 内部类 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;
        }
    }
    ...
}

返回目录

5-3 常用方法
public class LinkedList<E> ...{
    // 1 构造器
    // 无参构造器
    public LinkedList() {}
    // 有参构造器,复制元素
    public LinkedList(Collection<? extends E> c) {...}
    
    // 2 作为链表
	// 添加元素
    boolean add(E e);
    // 删除第一个符合条件的元素
    boolean remove(Object o);

    // 3 作为双端队列(不推荐)
    //在队头、队尾添加节点,有返回值
	public boolean offerFirst(E e) {...}
    public boolean offerLast(E e) {...}
    //删除并返回队头、队尾元素
    public E pollFirst() {...}
    public E pollLast() {...}
    //返回队头、队尾元素,但不删除
   	public E peekFirst() {...}
    public E peekLast() {...}
    
    // 4 作为栈(不推荐)
	//入栈
    public void push(E e) {...}
	//出栈
    public E pop() {...}
    //查看栈顶元素
    public E peek() {...} 
}

返回目录


6 PriorityQueue 类
6-1 介绍

(1)PriorityQueue 类实现了 Queue 接口,模拟了“优先队列”这个数据结构。

(2)其本质是小顶堆,通过数组实现该结构。

(3)不允许插入 null。

(4)非线程安全。

(5)需要传入 Comparator,或者保存的类对象实现了 Comparable。

返回目录

6-2 底层实现

PriorityQueue 类的底层使用 Object[] 数组存储数据。

用 Object[] 数组实现了完全二叉树,通过 Comparable 或者 Comparator 接口进行元素大小的比较。

public class PriorityQueue<E>...{
    ...
        
    transient Object[] queue;
        
    private final Comparator<? super E> comparator;
    
    // 添加元素
    // 不允许插入 null
	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);
    }
    
    // 使用 Comparator 进行元素比较,插入到完全二叉树的相应位置。
    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;
    }
    
    // 使用 Comparator 进行元素比较,插入到完全二叉树的相应位置。
    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;
    }
    ...
}

返回目录

6-3 扩容机制

(1)调用无参构造器初始化时,默认容量为 11;

(2)调用有参构造器初始化时,容量自定义,但是必须 >=2;

(3)当数组占满后,进行扩容;如果原来的容量 <64,则扩容为 (2 倍 + 2);如果原来的容量 >=64,则扩容为 1.5 倍。

public class PriorityQueue<E>...{
    ... 
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    transient Object[] queue;
    private final Comparator<? super E> comparator;
    
    // 无参构造器,调用有参构造器
    // 默认容量为 11
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
    
    // 有参构造器
    // 自定义容量,容量 >= 2
    public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

    // 添加元素
    // 不允许插入 null
	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;
    }
    
	// 扩容
    // 如果原来的容量 <64,则扩容为 (2 倍 + 2);
    // 如果原来的容量 >=64,则扩容为 1.5 倍;
    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);
    }
    ...
}

返回目录

6-4 常用方法
public class PriorityQueue<E>...{
	// 1 构造器
    // 无参构造器,默认容量为 11。元素需要实现 Comparable 接口。
    public PriorityQueue() {...}
    // 有参构造器,自定义容量。元素需要实现 Comparable 接口。
    public PriorityQueue(int initialCapacity) {...}
    // 有参构造器,传入比较器,默认容量为 11。使用该比较器进行排序。
    public PriorityQueue(Comparator<? super E> comparator) {...}
    // 有参构造器,自定义容量,传入比较器。
    public PriorityQueue(int initialCapacity,Comparator<? super E> comparator) {...}
    // 有参构造器,复制集合。
    public PriorityQueue(Collection<? extends E> c) {...}
    
    // 2 作为队列(优先队列)
    //将元素加入队尾
    boolean offer(E e);
    //删除并返回队头元素。如果队列为空,返回 null
    E poll();
    //获取队头元素,但不删除。如果队列为空,返回 null
    E peek();
}

返回目录

6-5 代码示例
  • 建立小顶堆
PriorityQueue<String> queue = new PriorityQueue<>((s1, s2) -> {
    return s1.length() - s2.length();
});
queue.offer("abc");
queue.offer("ab");
queue.offer("abcd");
queue.offer("a");
while(!queue.isEmpty()){
    System.out.println(queue.poll()); // a ab abc abcd
}
  • 建立大顶堆
PriorityQueue<String> queue = new PriorityQueue<>((s1, s2) -> {
    return s2.length() - s1.length();
});
queue.offer("abc");
queue.offer("ab");
queue.offer("abcd");
queue.offer("a");
while(!queue.isEmpty()){
    System.out.println(queue.poll()); // abcd abc ab a
}

返回目录


7 不同 Queue 集合的使用场景

ArrayDeque:底层是循环数组,增删改查效率较高。可以作为双端队列、栈来使用。推荐使用。

LinkedList:底层是双向链表,增删效率高,改查效率低。可以作为双端队列、栈来使用。

PriorityQueue:底层是数组实现的小顶堆,作为优先队列使用。

返回目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值