百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

谈谈集合.Queue

toyiye 2024-06-21 12:09 9 浏览 0 评论

Java中集合的主要作用就是装盛其他数据和实现常见的数据结构。所以当我们要用到“栈”、“队列”、“链表”和“数组”等常见的数据结构时就应该想到可以直接使用JDK给我们提供的集合框架。 比如说当我们想用到队列时就应该想到使用LinkedList和ArrayDeque 。本篇博客将介绍Collection框架中的Queue。

Queue接口继承了Collection接口,所以Collection的所有方法Queue的实现类中都包含,同时还有一个子接口Dqueue,表示双端队列。对于Queue我们主要掌握ArrayDeque和LinkedList,了解 PriorityQueue (优先级队列)。

1. 接口介绍

Queue也是Java集合框架中定义的一种接口,直接继承自 Collection 接口。除了基本的 Collection 接口规定测操作外,Queue 接口还定义一组针对队列的特殊操作。通常来说,Queue是按照先进先出(FIFO)的方式来管理其中的元素的,但是优先队列是一个例外。

Deque接口继承自Queue接口,但Deque支持同时从两端添加或移除元素,因此又被成为双端队列。鉴于此,Deque接口的实现可以被当作FIFO队列使用,也可以当作LIFO队列(栈)来使用。官方也是推荐使用 Deque的实现来替代Stack。Deque的主要实现类有ArrayDeque和LinkedList。

ArrayDeque是Deque接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque没有容量限制,可根据需求自动进行扩容。ArrayDeque不支持值为null的元素。

LinkedList的具体特性已经在之前的博客中介绍过了,这边不再重新介绍了。

1.1 Queue接口概览

Queue可以被当做一个队列来使用,实现FIFO操作,主要提供下面的操作

public interface Queue<E> extends Collection<E> {
    //向队列尾部插入一个元素,并返回true
    //如果队列已满,抛出IllegalStateException异常
    boolean add(E e);

    //向队列尾部插入一个元素,并返回true
    //如果队列已满,返回false
    boolean offer(E e);

    //取出队列头部的元素,并从队列中移除
    //队列为空,抛出NoSuchElementException异常
    E remove();

    //取出队列头部的元素,并从队列中移除
    //队列为空,返回null
    E poll();

    //取出队列头部的元素,但并不移除
    //如果队列为空,抛出NoSuchElementException异常
    E element();

    //取出队列头部的元素,但并不移除
    //队列为空,返回null
    E peek();
}

1.2 Deque接口

Deque接口是一个双端队列,可以对队列的头尾进行操作,所以也可以当做栈来使用。

下面的表格列举了Queue和Deque接口的相对应方法

Queue方法Deque方法add(e)addLast(e)offer(e)offerLast(e)remove()removeFirst()poll()pollFirst()element()getFirst()peek()peekFirst()

Deque还有一个重要的功能是可以当做栈来使用

Stack方法Deque方法push(e)addFirst(e)pop()removeFirst()peek()peekFirst()

2. ArrayDeque

ArrayDeque是Deque基于数组的实现。

以下内容来源于网络 博客

2.1 ArrayDeque的成员变量

//数组存储元素
    transient Object[] elements;
    //头部元素索引
    transient int head;
    //尾部元素索引
    transient int tail;
    //最小容量
    private static final int MIN_INITIAL_CAPACITY = 8;

ArrayDeque底层使用数组存储元素,同时还使用head和tail来表示索引,但注意tail不是尾部元素的索引,而是尾部元素的下一位,即下一个将要被加入的元素的索引。

2.2 初始化

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) {
    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
    }
    elements = new Object[initialCapacity];
}

这边讲下private void allocateElements(int numElements)这个方法。ArrayDeque的初始化容量必须是2^n。所以你传的初始化容量如果是10,那么实际申请的数组容量是16,如果申请的容量是33,那么实际的容量是62。如果申请的容量是62,那么实际申请的容量是128。

2.3 add方法

public void addFirst(E e) {
    if (e == null)
        throw new NullPointerException();
    elements[head = (head - 1) & (elements.length - 1)] = e;
    if (head == tail)
        doubleCapacity();
}

public void addLast(E e) {
    if (e == null)
        throw new NullPointerException();
    //tail中保存的是即将加入末尾的元素的索引
    elements[tail] = e;
    //tail向后移动一位
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        //tail和head相遇,空间用尽,需要扩容
        doubleCapacity();
}

在存储的过程中,这里有个有趣的算法,就是tail的计算公式(tail = (tail + 1) & (elements.length - 1)),注意这里的存储采用的是环形队列的形式,也就是当tail到达容量最后一个的时候,tail就为等于0,否则tail的值tail+1。

head也采用了类似的方式,每次在头部添加元素后,head都会指向最新被添加进去的那个元素所在的位置。当head小于0时也会采取环的形式存元素,比如head已经指向位置0,又向队列中头部添加一个元素后,head会变成length-1。

关于head和tail,需要主要的是head永远指向第一个元素的索引位置,tail永远指向尾部位置(这个位置上暂时还没有元素,如果在尾部插入元素,则在这个位置上插入)

2.4 扩容机制

private void doubleCapacity() {
    assert head == tail; //扩容时头部索引和尾部索引肯定相等
    int p = head;
    int n = elements.length;
    //头部索引到数组末端(length-1处)共有多少元素
    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;
}

下图是扩容的示意图

3. 总结

ArrayDeque是Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque 可以作为栈来使用,效率要高于Stack;ArrayDeque 也可以作为队列来使用,效率相较于基于双向链表的LinkedList也要更好一些。

所以我们程序中如果要使用到“队列”和“栈”这种数据结构,我们要首先想到LinkedList和ArrayDeque。个人认为作为队列和栈来使用的话,两者性能相差不大,但是ArrayDeque需要扩容,还需要申请连续的内存空间,所以个人更推荐使用LinkedList,不知道我的理解对不对。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码