【学习集合--Queue,Deque】

学习内容:

  1. Queue,Deque概述
  2. Queue集合实现–ArrayDeque

学习产出:

概述

在这里插入图片描述

Queue(队列),Deque(双端队列)

队列存储数据允许从结构的一端进行操作,并且从结构的另一端进行移除操作,进行入对的是尾部,出队的是头部。双端队列是指可以在一端进行入队操作,又可以进行出队操作的结构

注意:队列和双端队列都不允许在除了队列头部和尾部的其他位置上进行操作,在JCF中具有队列操作特性的集合都实现了java.util.Queue

除了java.util包下面的linkedList和ArrayDeque外,还有大量的实现在java.util.concurrent中。

要学习的ArrayQuque和PriorityQueue使用的分别是可循环的双指针数组和小顶堆


Queue集合实现–ArrayDeque

概述

ArrayDeque是基于数组(可扩容的数组)结构实现的双端队列,与普通的数组结构相比,可以有效减少扩容次数,线程不安全,不能在多线程场景中使用。
ArrayDuque既有队列的,双端队列的操作特点,又有栈结构的操作特点

ArrayDeque结构和相关方法

内部主要结构为一个循环数组,循环数组是一个固定大小的数组,并且定义了一个动态的有效数据范围(不会大于数组长度),只有在这个范围内数据才能被读写,这个范围不好受数组头部和尾部的限制。

 	transient Object[] elements;
 	//用来标识下一次移除操作的数据索引位
    transient int head;
    //下一次添加数据的位置
    transient int tail;

作为队列的主要方法

Column 1Column 2
add(e)在队列尾部添加新元素,不能为空,成功返回true
offer(e)与add(e)相同,成功返回true
remove(e)从队列头部移除数据,没有数据会抛异常
poll(e)从队列头部移除数据,没有数据返回null
element(e)获取队列头部数据,不会移除,没有则抛异常
peek(e)获取队列头部数据,不会移除,没有返回null

作为双端队列使用的主要方法

方法名方法说明
addLast(E)在队列尾部添加数据,插入的数据不能为空,否则抛异常
addFirst(E)在队列头部添加数据,插入的数据不能为空,否则抛异常
offerFirst(E)作用与addFirst(E)相同 ,就是调用addFirst(e),成功返回true
offerLast(E)与addLast(E)相同,就是调用addLast(e),成功返回true
getFirst()获取队列头部数据,没有会抛异常
getLast()获取队列尾部数据,没有会抛异常
peekFirst()获取队列头部数据,没有则返回null
peekLast()获取队列尾部数据,没有则返回null
removeFirst()移除队列头部对象,没有抛异常
removeLast()移除队列尾部对象,没有抛异常
pollFirst()移除双向链表头部节点,没有返回null
pollLast()移除双向链表尾部节点,没有返回null

作为栈结构使用

方法名方法说明
push()在栈结构头部添加数据,不能为空
pop()在栈结构头部移除数据,没有会抛异常
ArrayDeque初始化过程

三个重要的属性和三个构造方法

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{
    transient Object[] elements;
    transient int head;
    transient int tail;
    //集合容量上限
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


	//初始化一个长度为16+1的数组
    public ArrayDeque() {
        elements = new Object[16 + 1];
    }
	//设置集合的初始化容量,如果小于一,则初始化容量值为1的数组
    public ArrayDeque(int numElements) {
        elements =
            new Object[(numElements < 1) ? 1 :
                       (numElements == Integer.MAX_VALUE) ? Integer.MAX_VALUE :
                       numElements + 1];
    }
	//初始化成功后,向集合中添加数据引用
    public ArrayDeque(Collection<? extends E> c) {
        this(c.size());
        copyElements(c);
    }

1.8之后对内部逻辑做了比较大的修改,主要的思路是在循环数组这添加了一个空索引位置,用于表示在完成数据对象操作后是否需要进行扩容。对扩容过程也进行了调整,使数组的容量值不需要严格满足2的幂。

ArrayDeque集合添加操作

完成初始化之后,可以使用push(E),offerFirst(E),addFirst(E),addLast(E)等方法,在head和tail表示的数据范围的头部或者尾部添加新的数据对象了。使用什么样的操作取决于调用者以何种方式(队列,双端队列或者栈)。实际进行工作的方法只有两个addFirst(E),addLast(E)

addFirst方法
public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        final Object[] es = elements;
        //取得上一个索引栏位,并且就数据设置于上面
        //在将新位置的索引赋给head,将其成为新的头节点
        es[head = dec(head, es.length)] = e;
        //如果成立则说明添加操作后数组没有空余索引位,需要进行扩容操作
        if (head == tail)
            grow(1);
    }
static final int dec(int i, int modulus) {
//主要是确认0号索引位置的上一个位置
        if (--i < 0) i = modulus - 1;
        return i;
    }
addLast(E)方法
public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        final Object[] es = elements;
        es[tail] = e;
        //将下一个位置赋给tail,如果和head相等则扩容
        if (head == (tail = inc(tail, es.length)))
            grow(1);
    }
    //主要是确实索引为最后一位时的下一个位置
static final int inc(int i, int modulus) {
        if (++i >= modulus) i = 0;
        return i;
    }
ArrayDeque的扩容操作
 private void grow(int needed) {
        // overflow-conscious code
        final int oldCapacity = elements.length;
        int newCapacity;
        // Double capacity if small; else grow by 50%
        //根据是否大于64计算增量
        int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1);
        //增量小于最小需要扩容值或者新容量大于最大的数组容量值
        if (jump < needed
            || (newCapacity = (oldCapacity + jump)) - MAX_ARRAY_SIZE > 0)
            //计算新的容量值
            newCapacity = newCapacity(needed, jump);

				//下面是数组的复制,以及移动数据保证数据的结构
        final Object[] es = elements = Arrays.copyOf(elements, newCapacity);
        // Exceptionally, here tail == head needs to be disambiguated
        if (tail < head || (tail == head && es[head] != null)) {
            // wrap around; slide first leg forward to end of array
            int newSpace = newCapacity - oldCapacity;
            System.arraycopy(es, head,
                             es, head + newSpace,
                             oldCapacity - head);
            for (int i = head, to = (head += newSpace); i < to; i++)
                es[i] = null;
        }
    }

最后面的操作就是把从head到有数据的这一段(head到原数组这一段)移到最后,再把移动的数据设置为空。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值