java容器类4:Queue深入解读

Collection的其它两大分支:List和Set在前面已近分析过,这篇来分析一下Queue的底层实现。

前三篇关于Java容器类的文章:

java容器类1:Collection,List,ArrayList,LinkedList深入解读

java容器类2:Map及HashMap深入解读

java容器类3:set/HastSet/MapSet深入解读

Queue


public interface Queue<E> extends Collection<E> {
    boolean add(E var1);

    boolean offer(E var1);

    E remove();

    E poll();

    E element();

    E peek();
}
这就是Queue接口的代码,相比于List或者Set简洁明了很多。下面介绍一下它里面接口的含义:
在处理元素前用于保存元素的 collection。除了基本的 Collection 操作外,队列还提供其他的

插入、提取和检查

操作。每个方法都存在两种形式:

一种抛出异常(操作失败时),另一种返回一个特殊值(nullfalse,具体取决于操作)。

插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。



AbstractQueue



AbstractQueue中实现了queue和Collection中部分函数,比较简单,源码如下:

public abstract class AbstractQueue<E> extends AbstractCollection<E>
    implements Queue<E> {
    protected AbstractQueue() {
    }
    public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    public void clear() {
        while (poll() != null)
            ;
    }

    public boolean addAll(Collection<? extends E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
}

从上面的调用关系可以看出来,Queue的解释中哪些是会抛出异常的调用,哪些是不会抛出异常的调用接口。

Deque



一个线性 collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。(java 1.6版本中的家扣,1.8中接口有变动,但是大概含义相似)

Java容器类1中介绍了LinkedList,链表类其实实现了 Deque的接口,所以链表支持从头部和尾部添加和移除元素。

ArrayDeque



因为链表的存储结构可能比较简单,这里介绍一下ArrayDeque,它的里面存储元素使用一个数组。 作为一个双端队列的数组,涉及到扩容和元素的拷贝的逻辑可能比较复杂些。

看一下里面的几个构造函数:

public ArrayDeque() {
        elements = 

new Object[16];

    }

public ArrayDeque(int numElements) {
        

allocateElements(numElements);

    }

public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }

从构造函数可以看出默认的会分配一个长度为16的数组。同时,也支持指定大小的队列(这里的allocateElements函数之前在

java容器类2:Map及HashMap深入解读  中已经深入分析过,是个非常精妙的函数)。下面看一下到底是如何实现插入?又是如何自动扩充数组的?

ArrayQueue中维护了两个成员变量:head和tail分别代表 队列的头和尾在数组中的下标。

 /**
     * The index of the element at the head of the deque (which is the
     * element that would be removed by remove() or pop()); or an
     * arbitrary number equal to tail if the deque is empty.
     */
    transient int head;

    /**
     * The index at which the next element would be added to the tail
     * of the deque (via addLast(E), add(E), or push(E)).
     */
    transient int tail;

在队列的首部添加元素:

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();    elements[tail] = e;    

if

 ( (tail = (tail + 1) & (elements.length - 1)) == head)        doubleCapacity();}

由构造函数和数组分配的函数可以知道,数组的长度肯定是一个2的幂次方的一个整数。

当head为大于0的整数时,在头部插入很简单,将head前一个元素赋值为e就可以了。那么当head为0时,怎么计算的?由上面可以看出会插入到数组的尾部。所以ArrayDeque相当于在一个圆环上,规定一个头一个尾作为队列的前后(将数组的首位相连)。

在最后位置添加元素的原理和在首部添加相似。注意判断是否已满的 判断,这里不再分析。

当队列已经满后,会将数组的长度double。由于数组是不能自由扩张的,所以doubleCapacity函数应该是分配一个更大的数组,并将原来的元素拷贝进去,这里不再分析。

总的来说双端队列ArrayDeque是在数组的基础之上实现,原理和实现都不算复杂,但是很多边界调节等细节可以斟酌。

BlockingQueue


BlockingQueue是concurrent包下面的,后续打算写一个系列文章专门分析concurrent包下面的类,及一些多线程相关的东西。

PriorityQueue



优先级队列是一个可以排序的队列。内部是一个最大堆,大部分人应该了解堆排序,所以对最大堆应该不会陌生。

每次读取元素都是读取最大的元素(默认情况下)。

对外的接口有如下:

方法名 功能描述
add(E e) 添加元素
clear() 清空
contains(Object o) 检查是否包含当前参数元素
offer(E e) 添加元素
peek() 读取元素,(不删除)
poll() 取出元素,(删除)
remove(Object o) 删除指定元素
size() 返回长度

PriorityQueue 默认是一个最大堆结构,如果想构造一个最小堆:

private static final int DEFAULT_INITIAL_CAPACITY = 11;
PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(DEFAULT_INITIAL_CAPACITY, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });

关于堆的数据结构部分这里不再分析可以参考:https://www.cnblogs.com/tstd/p/5125949.html

c++版的优先级队列分析:优先级队列用法详解(priority_queue)

由于是通过数组保存数据,所以优先级队列也会涉及到容量的扩充等,和HashMap/Setting/Collection的扩容原理相同,甚至更简单,不再分析。PriorityQueue内部的操作都是在最大堆的基础上展开的,阅读堆的数据结构相关资料便可了解。

参考:

http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/Deque.html

http://www.cnblogs.com/NeilZhang/p/5650226.html

原文地址:https://www.cnblogs.com/NeilZhang/p/8623207.html

时间: 2024-10-09 21:39:05

java容器类4:Queue深入解读的相关文章

java容器类3:set/HastSet/MapSet深入解读

介绍 Set:集合,是一个不包含重复数据的集合.(A collection that contains no duplicate elements. ) set中最多包含一个null元素,否者包含了两个相同的元素,不符合定义. 上一篇学习了Java中的容器类的一些基础接口,以及Collection接口三大分支中的List分支(ArrayList以及LinkedList).这一篇文章将讲解Collection三大分支(List.Set.Queue)中的Set分支,以及衍生出来的子类. java容器

【转】java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别

原文网址:http://www.360doc.com/content/15/0427/22/1709014_466468021.shtml java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别. 经常会看到程序中使用了记录集,常用的有Collection.HashMap.HashSet.ArrayList,因为分不清楚它们之间的关系,所以在使用时经常会混淆,以至于不知道从何下手.在这儿作了一个小例

Java容器类基础

Java容器类基础 Java实用类库提供了一套相当完善的容器类,基本类型为List,Set,Map,Queue.它们都有一些特性,例如Set对于每一个值都只保存一个对象,Map允许你将对象和对象关联起来.此外,Java容器类都可以自动调节尺寸.因此,与数组不同,你可以放置任意数量的对象到容器中而不用担心容器应该设置为多大. Java容器类有4个接口,它们分别上面提到过的List,Set,Map,Queue:在理想情况下,你编写的代码大多数情况是在和这些接口打交道,并且你唯一需要指定所要使用精确类

Java容器类概述

1.简介 容器是一种在一个单元里处理一组复杂元素的对象.使用集合框架理论上能够减少编程工作量,提高程序的速度和质量,毕竟类库帮我们实现的集合在一定程度上时最优的.在Java中通过java.util为用户实现了一个Collection Framework,这个集合框架用统一的架构来表示和操作所有的集合,具体包含以下内容: interface:表示集合的抽象数据类型,它将容器的具体实现与提供的接口分离: implement:表示集合接口的具体实现: algorithms:对集合中的元素提供的一些泛型

Java容器类的深入理解

Java容器类包含List.ArrayList.Vector及map.HashTable.HashMap ArrayList和HashMap是异步的,Vector和HashTable是同步的,所以Vector和HashTable是线程安全的,而ArrayList和HashMap并不是线程安全的.因为同步需要花费机器时间,所以Vector和HashTable的执行效率要低于ArrayList和HashMap. Collection ├List       接口 │├LinkedList      

Java容器类接口的选择

我们知道Java容器类实际提供了四类接口:Map,List,Set和Queue,如下图所示,每种接口都有不止一个版本的实现,如果在实际编写程序时需要使用某种接口时该如何选择. 从Oracle的Java Language Best Practices 中的说明可以看到建议不要在新程序中使用HashTable,Vector,Stack使用HashMap,ArrayList和LinkedList来替换. ArrayList和LinkedList都实现了List接口,当ArrayList底层由数组实现,

java容器类的继承结构

摘要: java容器类的继承结构 Java容器类库定义了两个不同概念的容器,Collection和Map Collection 一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素.Set不能有重复元素.Queue按照排队规则来确定对象产生的顺序. (文中Jdk源码版本无特殊说明均为jdk1.8.0_101) public interface Collection<E> extends Iterable<E> { 可以看到,java定义了Collec

Java容器类类库基本概念详解

  Java容器类类库的用途是"保存对象",并将其划分为两个不同的概念: Collection.一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素,而Set不能有重复元素.Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同). Map.一组成对的"键值对"对象,允许你使用键来查找值.ArrayList允许你使用数字来查找值,因此在某种意义上讲,他将数字与对象关联在了一起.映射表允许我们使用另一个对象来查找某个对象

java容器类---概述

1.容器类关系图 虚线框表示接口. 实线框表示实体类. 粗线框表示最经常使用的实体类. 点线的箭头表示实现了这个接口. 实线箭头表示类能够制造箭头所指的那个类的对象. Java集合工具包位于Java.util包下.包括了非常多经常使用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致能够分为例如以下五个部分:List列表.Set集合.Map映射.迭代器(Iterator.Enumeration).工具类(Arrays.Collections). 从上图中能够看出,集合