今天我要分享的是java里面比较常见的数据结构队列的源码分析,队列,先进先出模式,即FIFO的特色,平常生活中队列的特色也随处可见,超市购物排队,餐厅排队买饭等一系列都知足了队列的先进先出的特色,java做为一门高级语言,天然提供了队列这种成熟的数据结构供咱们使用了。
java
今天我就以本身的理解进行分析了,首先先说下或者先讲述下我为何要写这篇文章吧?工做中用到了?没有,那你为何要写?炫技?也不是,主要是以前我本身分析了ArrayList,LinkedList以及Stack的源码文章了,到这里就理所应当的应该分析队列的这种数据结构了,知足一下学生时代心心念的数据结构吧。nginx
数据结构,数组,链表,栈,队列等在咱们的开发中很常见,可是我没有用队列的特色作过业务的需求开发,因此这篇文章的讲述天然就涉及不到工做的内容了。面试
说了这么多,接下来就逐渐去分析队列的源码吧,写到这时下起了小雨,对,这个时间段是晚上十点左右,这篇文章是本身继五一放假来的第一篇文章,本身玩着玩着手机就忽然想起了要写这篇文章了,索性就过来写了,要是学生时代这么努力多好,其实我不后悔学生时期的贪玩,即便那个时间段努力增进本身的技术又能怎样?谁也不知道将来会怎样,不后悔,便值得。数据库
关于读源码,如何进行梳理整个过程,每一个人都有着本身的一套,在这里我就以本身的一套来进行分析好了。絮叨了这么多,接下来就是示例程序的展现了,今天看了一位做者的说法,他说写一篇文章,代码不易过多,每一个人都有着本身的写做方式,见仁见智吧,适合本身的才是最好的,好怀念写代码的那段日子,没日没夜。数组
这里要分析的是下面这个队列,因此这里暂时下贴出一点这个类的继承结构,便于本身分析。
微信
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable{}
一,首先咱们先看下ArrayDequeue队列的构造函数吧。
数据结构
/** * Constructs an empty array deque with an initial capacity * sufficient to hold 16 elements. */ public ArrayDeque() { elements = new Object[16]; }
构造一个内存容量为16大小的数组,用于存储数据元素,即队列的初始大小容量为16,能够容纳16个数据元素。
jvm
二,ArrayDequeue也支持自定义容量的构造函数的设置,和ArrayList集合差很少的意思,这样减小了数组元素的拷贝,提升了性能,通常面试的时候都会问到这一点的。
函数
public ArrayDeque(int numElements) { allocateElements(numElements); } 步骤一: private void allocateElements(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>> 1); initialCapacity |= (initialCapacity >>> 2); initialCapacity |= (initialCapacity >>> 4); initialCapacity |= (initialCapacity >>> 8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++;
if (initialCapacity < 0) initialCapacity >>>= 1; } elements = new Object[initialCapacity];//暂时先看这部分好了,就是内存空间的开辟,上面的内容暂时先不理解也没事,不影响咱们的分析 }
写到这里,写到了内存空间的分配的字样,想到了本身学习c语言的模样,那个拿着大部书《C语言程序设计》前往机房的少年,因为兴趣使然,逐渐走上了javaWeb的开发了,不过这里说明一点,学习c语言对于你理解高级语言java大有裨益,这是本身的理解。
源码分析
三,通常写到这里就会去分析数据结构的基本方法,添加方法add了,这里固然是顺势而为分析一下add方法了。
public boolean add(E e) { addLast(e); return true; } 步骤一:public void addLast(E e) { if (e == null) throw new NullPointerException(); elements[tail] = e; if ( (tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity(); }
首先进行前置校验,判断元素e是否为null,如果null直接抛出空指针异常,提及前置校验,其实你在作javaWeb的开发时必定会遇到的,基于aop的方式或者手动判断都是要的,由于咱们要去校验不可信数据即外部输入的数据,关于如何校验,这里暂时说一下,本身后面会单独写上一篇关于参数校验的文章了,而后将元素e放置到数据的末尾,而后判断队列是否满了,若满了则进行扩容,这也是为何叫动态扩容机制了,如今谁还在使用静态扩容呢?况且java做为一门高级语言呢,顺势而为成就了这个语言使人喜欢的特色吧。
四,队列既然有入队,想必就会想到队列出队的方法,即poll方法,接下来咱们继续看下队列出队的方法时间吧。
public E poll() { return pollFirst(); } 步骤一: public E pollFirst() { int h = head; @SuppressWarnings("unchecked") E result = (E) elements[h]; // Element is null if deque empty if (result == null) return null; elements[h] = null; // Must null out slot head = (h + 1) & (elements.length - 1); return result; }
首先将队头元素赋值给局部变量h,即int h=head,这也是一向写代码的习惯,而后根据数组下标值进行元素的定位,即时间复杂度为O(1)这也是数组做为查找比较快的缘由之一吧,写到这写到了hash函数的特性了,时间复杂度也很小,后面如果有须要能够分享一下关于hash相关的内容,毕竟数据结构map的特色就是结合hash加上树,链表,数组组成了优秀的源码。而后将队列的队头元素置为null,这样便于垃圾收集器进行资源的回收,可是这里没有写到let's gc ,怎么和集合不同呢,而后队列元素前置一位,而且判断队列是否在整个队列的范围内,这是比较重要的,将获取的元素进行返回。
五,有了入队,出队,接下来咱们在看下如何获取元素个数
public int size() { return (tail - head) & (elements.length - 1); }
队尾元素位置减去队首元素位置进行队列元素个数的肯定,是否是很简单,写到这是否是有点激动能够写本身的队列了,其实造轮子当然使人兴奋,可是为时尚早,慢慢来,须要的时候天然要造轮子的。
六,通常集合都会有判空操做,否则在操做元素时发现是空的,也没有什么意义了,这也是算前置校验的一种吧。
public boolean isEmpty() { return head == tail; }
队首位置等于队尾位置就表示这个队列为空,是否是很容易理解。
七,通常集合数据结构咱们都会去判断某个元素是否存在,这样便于咱们的查找,说到这没有数据库咱们同样能够去操做数据,不过都是基于内存来操做的,数据库的出现能够将数据永远存储到存储介质中,保证了断电不易丢失的特性,这也是它普遍的用途吧。
public boolean contains(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) return true; i = (i + 1) & mask; } return false; }
首先,先将元素元素o与null进行比对,若为null则直接返回false,由于队列不容许添加元素为null的值,因此这里又作了一下前置校验,加快校验的速度,而后循环判断元素o和队列的每个元素进行比较,此时时间复杂度为O(n),相等则返回true,不然返回false。
八,队列这个数据结构能够像栈同样只返回队列的队首元素,可是不出队。
public E peek() { return peekFirst(); } 步骤一: public E peekFirst() { // elements[head] is null if deque empty return (E) elements[head]; }
这里只返回数组的第一个元素,可是不出队,是否是很巧妙,这是设计的构思,欣赏一下。
九,队列的操做其实也是增删改查操做,这里介绍最后一个方法,如何清空队列的元素。
public void clear() { int h = head; int t = tail; if (h != t) { // clear all cells head = tail = 0; int i = h; int mask = elements.length - 1; do { elements[i] = null; i = (i + 1) & mask; } while (i != t); } }
首先使用两个局部变量h,t进行队首,队尾元素位置的肯定,使用do...while语句进行判断队首是否等于队尾来判断队列是否为空,而后将数组的每个元素置为null,而后等jvm垃圾收集器进行收集元素为null,这样队列的元素就被清空了。
十,到这里就结束了本身对队列的源码分析,其实你会发现我这里没有对队列的每个方法进行分析,其实都差很少,这里起到一个开头做用就能够了,下面的每一个分析方法都差很少。
时间也不早了,也快到晚上十一点了,到这里就结束了,本身写文章的风格渐渐变了,变得文字稍微多了一点。
本文分享自微信公众号 - WwpwW()。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。