Java集合--Queue(Java中实现1)

1.2 Java中的实现

上一篇,阐述了队列的实现结构,通过图片的形式让大家有了更进一步的了解。

接下来,我,我们来看看队列在Java具体是如何成仙了,来看下Queue的代码!!!

在Java中,ArrayDeque、LinkedList、PriorityQueue等类实现了Queue接口,模拟了队列的数据结构。

其中,PriorityQueue是Queue直接子类实现,在原有基础上实现了元素的排序功能。

除此之外,Queue还有一个子接口--Deque,对Queue进行了扩展,定义了头尾操作功能,既可在队头添加(删除)元素,也可在队尾添加(删除)元素,俗称“双端队列”。

接下来,我们来具体介绍下这几个实现类。

1.2.1 Queue源码(基于JDK1.7.0_45)

在说Queue实现类之前,我们首先来了解下Queue到底提供了哪些功能!

//接口Queue:
public interface Queue<E> extends Collection<E> {
    //将指定元素插入到队列的尾部(队列满了话,会抛出异常)
    boolean add(E e);

    //将指定元素插入此队列的尾部(队列满了话,会返回false)
    boolean offer(E e);

    /返回取队列头部的元素,并删除该元素(如果队列为空,则抛出异常)
    E remove();

    //返回队列头部的元素,并删除该元素(如果队列为空,则返回null)
    E poll();

    //返回队列头部的元素,不删除该元素(如果队列为空,则抛出异常)
    E element();

    //返回队列头部的元素,不删除该元素(如果队列为空,则返回null)
    E peek();
}

在Queue源码中,定义了队列的基本操作--在队尾插入元素,在队头获取(删除)元素;

1.2.2 PriorityQueue源码(基于JDK1.7.0_45)

作为Queue的直接子类,PriorityQueue实现了Queue定义的方法。

不过,又与传统的队列不相。传统队列实现了“先进先出”数据模型,而PriorityQueue则实现了最小的元素优先出队,剩余元素依次按照大小顺序出队。

这就是所谓的“优先级队列”---元素按照任意的顺序插入,却总是按照顺序进行输出;每次从优先队列中取出来的元素要么是最大值,要么是最小值。接下来,我们来看下PriorityQueue具体是如何实现的:

PriorityQueue成员变量和构造方法:

public class PriorityQueue<E> extends AbstractQueue<E>
        implements java.io.Serializable {

    private static final long serialVersionUID = -7720805057305804111L;

    //默认初始化数组大小:
    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    //队列底层数据结构:数组
    private transient Object[] queue;

    //队列长度:
    private int size = 0;

    //实现元素排序的比较器:
    private final Comparator<? super E> comparator;

    //对queue的操作次数:
    private transient int modCount = 0;

    //默认构造函数:
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

    //可设置队列长度的构造函数:
    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

    //可设置队列长度、元素比较器的构造函数:
    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

PriorityQueue新增元素:

    //队列添加元素,底层调用offer:插入失败抛出异常
    public boolean add(E e) {
        return offer(e);
    }

    //队列添加元素: 插入失败返回false
    public boolean offer(E e) {
        //不支持添加为null的元素:
        if (e == null)
            throw new NullPointerException();

        //队列操作数+1:
        modCount++;
        int i = size;

        //队列长度 >= 数组长度时,扩容:
        if (i >= queue.length)
            grow(i + 1);

        //队列长度+1
        size = i + 1;

        //i==0,在数组角标为0处插入第一个元素:
        if (i == 0)
            queue[0] = e;
        else
            //插入的不是第一个元素:
            siftUp(i, e);
        return true;
    }

    //对队列底层数组扩容:
    private void grow(int minCapacity) {
        //现阶段数组长度:
        int oldCapacity = queue.length;
        
        //计算新数组的长度:
        // 如果 现阶段数组长度<64,则扩容为现阶段长度的2倍+2;
        // 如果 现阶段数组>=64,则扩容为现阶段长度的2倍+5;
        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)
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

PriorityQueue获取队列头部元素:

    //返回队列头部的元素,不删除该元素(如果队列为空,则返回null)
    public E peek() {
        if (size == 0)
            return null;
        return (E) queue[0];
    }

    //返回队列头部的元素,并删除该元素(如果队列为空,则返回null)
    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;
    }

PriorityQueue中核心方法:使用了比较器进行元素比较,当插入或者删除的元素后,对PriorityQueue中树的结构进行调整;

    private void siftUp(int k, E x) {
        //元素比较器不为null:
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            //元素比较器为null:
            siftUpComparable(k, x);
    }

    //进行堆结构的siftUp运算:使用元素比较器
    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;
    }

    //进行堆结构的siftUp运算:使用自定义元素比较器
    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;
    }

    private void siftDown(int k, E x) {
        //元素比较器不为null:
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            //元素比较器为null:
            siftDownComparable(k, x);
    }

    //进行堆结构的siftDown运算:使用元素比较器
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super 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 &&
                    ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

    //进行堆结构的siftDown运算:使用自定义元素比较器
    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;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

    //返回队列中的比较器:
    public Comparator<? super E> comparator() {
        return comparator;
    }
}

从上面的源码中,可以看出,PriorityQueue是由“堆结构”来实现的队列。而“堆结构”又是通过数组形成的一颗完全二叉树。所以,我们在代码中可以看到PriorityQueue最底层数据结构就是数组。

经过上面的源码的分析,对PriorityQueue的总结如下:

PriorityQueue是线程不安全的队列;

PriorityQueue中不允许插入null元素;

PriorityQueue是一个用“堆结构”来实现的队列;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容