文章目录

  • 概述

概述

一般我们认为队列都是先进先出的,在Java这里是非常灵活的。文档中有一句话是非常经典的:
Queues typically, but do not necessarily, order elements in a FIFO (first-in-first-out) manner. Among the exceptions are priority queues, which order elements according to a supplied comparator, or the elements’ natural ordering, and LIFO queues (or stacks) which order the elements LIFO (last-in-first-out). Whatever the ordering used, the head of the queue is that element which would be removed by a call to {@link #remove()} or {@link #poll()}. In a FIFO queue, all new elements are inserted at the tail of the queue. Other kinds of queues may use different placement rules. Every {@code Queue} implementation must specify its ordering properties.

意思是:
队列通常(但不一定)以FIFO(先进先出)方式对元素排序。例外情况包括优先队列(根据提供的比较器对元素排序)和LIFO队列(或堆栈)(后进先出)。无论使用什么顺序,队列的头部都是通过调用{@link #remove()}或{@link #poll()}来删除的元素。在FIFO队列中,所有新元素都插入到队列的尾部。其他类型的队列可能使用不同的放置规则。每个{@code Queue}实现都必须指定其有序属性。

Queue只是一个接口,具体的实现要靠子类来完成,如果想要使用优先队列可以选择 PriorityQueue ,如果仅仅就想用到队列的普通功能一般会选择LinkedList

我们看一下Queue接口的方法
(这里忽略插入类型不满足,插入的元素是空指针等的情况)

public interface Queue<E> extends Collection<E> {boolean add(E e);//如果可以在不违反容量限制的情况下立即将指定的元素插入此队列,成功后返回{@code true},如果当前没有空间可用,则抛出{@code IllegalStateException}。boolean offer(E e);//如果可以在不违反容量限制的情况下立即将指定的元素插入到此队列中。当使用受容量限制的队列时,此方法通常比{add}更可取,因为{add}会因为抛出异常而导致插入失败。//对比//add()和offer()都是向队列中添加一个元素。一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,调用 add()方法就会抛出一个 unchecked 异常,而调用 offer()方法会返回 false。因此就可以在程序中进行有效的判断! E remove();E poll();//类似于add()和offer()的关系//remove()和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况。E element();E peek();//类似于add()和offer()的关系//element()和 peek() 用于在队列的头部检索元素(不删除),返回值是头部元素。在队列为空时, element()抛出一个异常,而 peek()返回 null。
}

上面很详细地说明了Queue接口定义的方法的要求的功能

下面看看PriorityQueue 它继承自Queue,为优先队列。优先队列是基于堆的

链表,二叉查找树,都可以提供插入和删除最大值或者最小值这两种操作,但是为什么不用它们却偏偏使用堆。原因在于应用前两者需要较高的时间复杂度。

对于链表的实现,插入需要O(1),删除最小或最大需要遍历链表,故需要O(N)。

当然这个看实现方式 ,插入与删除最大或最小 难以兼得

对于二叉查找树,这两种操作都需要O(logN);而且随着不停的删除最小或最大的操作,二叉查找树会变得非常不平衡;同时使用二叉查找树有些浪费,因此很多操作根本不需要。

对于堆这两种操作的最坏时间复杂度为O(N),而插入的平均时间复杂度为常数时间,即O(1)。

下面让我们看一下源码:
字段部分:

@SuppressWarnings("unchecked")
public class PriorityQueue<E> extends AbstractQueue<E>implements java.io.Serializable {private static final long serialVersionUID = -7720805057305804111L;   //序列化版本IDprivate static final int DEFAULT_INITIAL_CAPACITY = 11;transient Object[] queue; // 用来存放元素int size;  //优先队列的大小private final Comparator<? super E> comparator;  //比较器,用于元素排序transient int modCount;     // 与迭代器相关

对transient的解释:
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。

它的构造方法 主要涉及到优先队列的创建(含大小的分配,默认为11)
这里展示了构造方法初始化优先队列的情况 以及相关的方法

关于c instanceof SortedSet<?> 是判断c是否是 SortedSet<?>的实例

以及c.getClass() == PriorityQueue.class 意思是c对应的Class类是否是PriorityQueue
一个类无论如何有多少个实例,这些实例都只对应一个Class类 也可以通过这个方法来判断一个实例是不是另一个类的实例

 //构造方法 创建一个初始容量为11的优先队列public PriorityQueue() {this(DEFAULT_INITIAL_CAPACITY, null);  //第二个参数是比较器}//构造方法 创建一个初始容量为指定值的优先队列public PriorityQueue(int initialCapacity) {  //第二个参数是比较器this(initialCapacity, null);}// 使用默认初始容量和创建PriorityQueue  其元素按照指定的比较器排序。public PriorityQueue(Comparator<? super E> comparator) {this(DEFAULT_INITIAL_CAPACITY, comparator);}//使用指定的初始容量创建PriorityQueue,该初始容量根据指定的比较器对其元素进行排序。public PriorityQueue(int initialCapacity,Comparator<? super E> comparator) {// 实际上并不需要这样的限制,但是对于JDK1.5兼容性来说,这种限制仍然存在,应该是当时Java编译器上的兼容性问题if (initialCapacity < 1)throw new IllegalArgumentException();this.queue = new Object[initialCapacity];this.comparator = comparator;}//创建一个{PriorityQueue}包含指定集合中的元素。如果指定的集合是一个{SortedSet}的实例,或者是另一个{PriorityQueue},这个优先队列将按照相同的顺序排序。否则,该优先队列将根据其元素的{ Comparable natural ordering}进行排序。public PriorityQueue(Collection<? extends E> c) {if (c instanceof SortedSet<?>) {SortedSet<? extends E> ss = (SortedSet<? extends E>) c;  this.comparator = (Comparator<? super E>) ss.comparator();   //设置比较器initElementsFromCollection(ss);    //具体的实现方法在后面 }else if (c instanceof PriorityQueue<?>) {PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c; this.comparator = (Comparator<? super E>) pq.comparator();  //设置比较器initFromPriorityQueue(pq); //具体的实现方法在后面 }else {this.comparator = null;initFromCollection(c); //具体的实现方法在后面 }}//创建一个{PriorityQueue},其中包含指定优先队列中的元素。这个优先队列将按照与给定优先队列相同的顺序排序。public PriorityQueue(PriorityQueue<? extends E> c) {this.comparator = (Comparator<? super E>) c.comparator();   //设置比较器initFromPriorityQueue(c);  //具体的实现方法在后面 }//创建一个{PriorityQueue},其中包含指定排序集中的元素。这个优先队列将按照与给定排序集中相同的顺序排序。public PriorityQueue(SortedSet<? extends E> c) {this.comparator = (Comparator<? super E>) c.comparator();  //设置比较器initElementsFromCollection(c);  //具体的实现方法在后面 }/** 确保 queue[0] 存在 **/private static Object[] ensureNonEmpty(Object[] es) {return (es.length > 0) ? es : new Object[1];}private void initFromPriorityQueue(PriorityQueue<? extends E> c) {if (c.getClass() == PriorityQueue.class) {    this.queue = ensureNonEmpty(c.toArray());  //复制元素 this.size = c.size(); //复制大小} else {initFromCollection(c);}}private void initElementsFromCollection(Collection<? extends E> c) {Object[] es = c.toArray();   //复制元素 为了后面的处理int len = es.length;        //复制大小 为了后面的处理// If c.toArray incorrectly doesn't return Object[], copy it.//如上句  就是排错的if (es.getClass() != Object[].class) es = Arrays.copyOf(es, len, Object[].class);//长度为1||构造器不为空 的情况下遍历元素 确保元素没有空值 if (len == 1 || this.comparator != null)for (Object e : es)if (e == null)throw new NullPointerException();this.queue = ensureNonEmpty(es);  //复制元素 this.size = len;  //复制大小}//使用给定集合中的元素初始化队列数组。private void initFromCollection(Collection<? extends E> c) {initElementsFromCollection(c);  //初始化heapify();  //重新建堆}//返回包含此队列中所有元素的数组。元素没有特定的顺序。public Object[] toArray() {return Arrays.copyOf(queue, size);}//返回包含此队列中所有元素的数组; //返回数组的类型是运行时的类型。 这个跟数组协变有关系//public <T> T[] toArray(T[] a) {final int size = this.size;if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(queue, size, a.getClass());System.arraycopy(queue, 0, a, 0, size);if (a.length > size)a[size] = null;  return a;}

上面的这段源码结尾部分有heapify()这个方法,这个会在后面去说

下面关注优先队列的方法了 开头部分出现一个字段

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

这是限制要分配的数组的最大大小。一些虚拟机在数组中保留一些头信息。试图分配更大的数组可能会导致OutOfMemoryError:
请求的数组大小超过V虚拟机的限制。 除了主流的Oracle 的虚拟机,Java的虚拟机还有别的种类。 这个字段自然与下面的扩容相关的方法密切相关

//增加数组的容量。传入的值是minCapacity  这个参数对后面的理解很关键private void grow(int minCapacity) {int oldCapacity = queue.length;  //旧容量大小// Double size if small; else grow by 50%//如果旧容量大小小于64  就加2 //如果旧容量大小>=64 就乘以2  >>是右移一位呀int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));// overflow-conscious codeif (newCapacity - MAX_ARRAY_SIZE > 0) //要求的新容量值大于最大限制了newCapacity = hugeCapacity(minCapacity); queue = Arrays.copyOf(queue, newCapacity);}//判断传入的值 是小于0 还是大于最大限制值的问题 private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

优先队列核心方法

    //*********************************************//将指定的元素插入此优先队列。public boolean add(E e) {return offer(e);  //这里用到了offer方法}//将指定的元素插入此优先队列。public boolean offer(E e) {if (e == null)throw new NullPointerException();modCount++;int i = size;if (i >= queue.length)grow(i + 1);siftUp(i, e);size = i + 1;return true;}//对于优先队列而言 offer和add方法是相同的//*********************************************//获取优先队列开头的元素 public E peek() {return (E) queue[0];  }//这个是配合下面remove方法的 遍历寻找数组中第一个满足条件的元素 有的话返回索引 没有的话返回-1private int indexOf(Object o) {if (o != null) {final Object[] es = queue;for (int i = 0, n = size; i < n; i++)if (o.equals(es[i]))return i;}return -1;}public boolean remove(Object o) {int i = indexOf(o); //获取索引if (i == -1)return false; else {removeAt(i);  //移除索引为i的元素 具体实现看下面return true;}}//如果此队列包含指定的元素,则返回{true}。更正式地说,如果且仅当此队列包含至少一个元素{ e},使得{ o.equals(e)},则返回{true}。public boolean contains(Object o) {return indexOf(o) >= 0;}
//返回大小public int size() {return size;}//从该优先级队列中删除所有元素。此调用返回后,队列将为空。public void clear() {modCount++;final Object[] es = queue;for (int i = 0, n = size; i < n; i++)es[i] = null;size = 0;  //大小设置为0}//删除第一个元素并且重建堆public E poll() {final Object[] es;final E result;if ((result = (E) ((es = queue)[0])) != null) {modCount++;final int n;final E x = (E) es[(n = --size)];es[n] = null;if (n > 0) {final Comparator<? super E> cmp;if ((cmp = comparator) == null)siftDownComparable(0, x, es, n);elsesiftDownUsingComparator(0, x, es, n, cmp);}}return result;}//移除特定位置的元素 并且重建堆E removeAt(int i) {// assert i >= 0 && i < size;final Object[] es = queue;modCount++;int s = --size;if (s == i) // removed last elementes[i] = null;else {E moved = (E) es[s];es[s] = null;siftDown(i, moved);if (es[i] == moved) {siftUp(i, moved);if (es[i] != moved)return moved;}}return null;}//堆的上升   选择比较器 进行重建堆private void siftUp(int k, E x) {if (comparator != null)siftUpUsingComparator(k, x, queue, comparator);elsesiftUpComparable(k, x, queue);}//堆上升的比较方法  从方法中可以看出堆是从索引为0的地方开始的 而且这是一个private static <T> void siftUpComparable(int k, T x, Object[] es) {Comparable<? super T> key = (Comparable<? super T>) x;while (k > 0) {int parent = (k - 1) >>> 1;  //a >>> b  a表要操作数 b表要移的位数 Object e = es[parent];if (key.compareTo((T) e) >= 0)break;es[k] = e;k = parent;}es[k] = key;}//使用特定的比较器进行比较private static <T> void siftUpUsingComparator(int k, T x, Object[] es, Comparator<? super T> cmp) {while (k > 0) {int parent = (k - 1) >>> 1;Object e = es[parent];if (cmp.compare(x, (T) e) >= 0)break;es[k] = e;k = parent;}es[k] = x;}//堆的下沉  选择比较器 进行重建堆private void siftDown(int k, E x) {if (comparator != null)siftDownUsingComparator(k, x, queue, size, comparator);elsesiftDownComparable(k, x, queue, size);}private static <T> void siftDownComparable(int k, T x, Object[] es, int n) {// assert n > 0;Comparable<? super T> key = (Comparable<? super T>)x;int half = n >>> 1;           // loop while a non-leafwhile (k < half) {int child = (k << 1) + 1; // assume left child is leastObject c = es[child];int right = child + 1;if (right < n &&((Comparable<? super T>) c).compareTo((T) es[right]) > 0)c = es[child = right];if (key.compareTo((T) c) <= 0)break;es[k] = c;k = child;}es[k] = key;}private static <T> void siftDownUsingComparator(int k, T x, Object[] es, int n, Comparator<? super T> cmp) {// assert n > 0;int half = n >>> 1;while (k < half) {int child = (k << 1) + 1;Object c = es[child];int right = child + 1;if (right < n && cmp.compare((T) c, (T) es[right]) > 0)c = es[child = right];if (cmp.compare(x, (T) c) <= 0)break;es[k] = c;k = child;}es[k] = x;}//在整个树中重建堆,不考虑调用之前元素的顺序private void heapify() {final Object[] es = queue;int n = size, i = (n >>> 1) - 1;final Comparator<? super E> cmp;if ((cmp = comparator) == null)for (; i >= 0; i--)siftDownComparable(i, (E) es[i], es, n);elsefor (; i >= 0; i--)siftDownUsingComparator(i, (E) es[i], es, n, cmp);}//返回用于对该队列中的元素排序的比较器,如果该队列按照其元素的{Comparable natural order}排序,则返回{ null}public Comparator<? super E> comparator() {return comparator;}

剩下的内容就是和迭代器相关的了,以后会详细对迭代器有个全面的介绍 贴出源码供参考

     //查阅优先队列的继承体系,它继承自collection,collection最上面继承了Itr 这个是与 Itr.remove.有管的方法void removeEq(Object o) {final Object[] es = queue;for (int i = 0, n = size; i < n; i++) {if (o == es[i]) {removeAt(i);break;}}}//迭代器部分 暂且不说以后会详细说一下的public Iterator<E> iterator() {return new Itr();}private final class Itr implements Iterator<E> {private int cursor;private int lastRet = -1;private ArrayDeque<E> forgetMeNot;private E lastRetElt;private int expectedModCount = modCount;Itr() {}                        // prevent access constructor creationpublic boolean hasNext() {return cursor < size ||(forgetMeNot != null && !forgetMeNot.isEmpty());}public E next() {if (expectedModCount != modCount)throw new ConcurrentModificationException();if (cursor < size)return (E) queue[lastRet = cursor++];if (forgetMeNot != null) {lastRet = -1;lastRetElt = forgetMeNot.poll();if (lastRetElt != null)return lastRetElt;}throw new NoSuchElementException();}public void remove() {if (expectedModCount != modCount)throw new ConcurrentModificationException();if (lastRet != -1) {E moved = PriorityQueue.this.removeAt(lastRet);lastRet = -1;if (moved == null)cursor--;else {if (forgetMeNot == null)forgetMeNot = new ArrayDeque<>();forgetMeNot.add(moved);}} else if (lastRetElt != null) {PriorityQueue.this.removeEq(lastRetElt);lastRetElt = null;} else {throw new IllegalStateException();}expectedModCount = modCount;}}//将此队列保存到流中(即序列化它)。private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {// Write out element count, and any hidden stuffs.defaultWriteObject();// Write out array length, for compatibility with 1.5 versions.writeInt(Math.max(2, size + 1));// Write out all elements in the "proper order".final Object[] es = queue;for (int i = 0, n = size; i < n; i++)s.writeObject(es[i]);}//从流(即反序列化)中重新构造{PriorityQueue}实例。private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in size, and any hidden stuffs.defaultReadObject();// Read in (and discard) array lengths.readInt();SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);final Object[] es = queue = new Object[Math.max(size, 1)];// Read in all elements.for (int i = 0, n = size; i < n; i++)es[i] = s.readObject();// Elements are guaranteed to be in "proper order", but the// spec has never explained what that might be.heapify();}public final Spliterator<E> spliterator() {return new PriorityQueueSpliterator(0, -1, 0);}final class PriorityQueueSpliterator implements Spliterator<E> {private int index;            // current index, modified on advance/splitprivate int fence;            // -1 until first useprivate int expectedModCount; // initialized when fence set/** Creates new spliterator covering the given range. */PriorityQueueSpliterator(int origin, int fence, int expectedModCount) {this.index = origin;this.fence = fence;this.expectedModCount = expectedModCount;}private int getFence() { // initialize fence to size on first useint hi;if ((hi = fence) < 0) {expectedModCount = modCount;hi = fence = size;}return hi;}public PriorityQueueSpliterator trySplit() {int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;return (lo >= mid) ? null :new PriorityQueueSpliterator(lo, index = mid, expectedModCount);}public void forEachRemaining(Consumer<? super E> action) {if (action == null)throw new NullPointerException();if (fence < 0) { fence = size; expectedModCount = modCount; }final Object[] es = queue;int i, hi; E e;for (i = index, index = hi = fence; i < hi; i++) {if ((e = (E) es[i]) == null)break;      // must be CMEaction.accept(e);}if (modCount != expectedModCount)throw new ConcurrentModificationException();}public boolean tryAdvance(Consumer<? super E> action) {if (action == null)throw new NullPointerException();if (fence < 0) { fence = size; expectedModCount = modCount; }int i;if ((i = index) < fence) {index = i + 1;E e;if ((e = (E) queue[i]) == null|| modCount != expectedModCount)throw new ConcurrentModificationException();action.accept(e);return true;}return false;}public long estimateSize() {return getFence() - index;}public int characteristics() {return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.NONNULL;}}/*** @throws NullPointerException {@inheritDoc}*/public boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);return bulkRemove(filter);}/*** @throws NullPointerException {@inheritDoc}*/public boolean removeAll(Collection<?> c) {Objects.requireNonNull(c);return bulkRemove(e -> c.contains(e));}/*** @throws NullPointerException {@inheritDoc}*/public boolean retainAll(Collection<?> c) {Objects.requireNonNull(c);return bulkRemove(e -> !c.contains(e));}// A tiny bit set implementationprivate static long[] nBits(int n) {return new long[((n - 1) >> 6) + 1];}private static void setBit(long[] bits, int i) {bits[i >> 6] |= 1L << i;}private static boolean isClear(long[] bits, int i) {return (bits[i >> 6] & (1L << i)) == 0;}/** Implementation of bulk remove methods. */private boolean bulkRemove(Predicate<? super E> filter) {final int expectedModCount = ++modCount;final Object[] es = queue;final int end = size;int i;// Optimize for initial run of survivorsfor (i = 0; i < end && !filter.test((E) es[i]); i++);if (i >= end) {if (modCount != expectedModCount)throw new ConcurrentModificationException();return false;}// Tolerate predicates that reentrantly access the collection for// read (but writers still get CME), so traverse once to find// elements to delete, a second pass to physically expunge.final int beg = i;final long[] deathRow = nBits(end - beg);deathRow[0] = 1L;   // set bit 0for (i = beg + 1; i < end; i++)if (filter.test((E) es[i]))setBit(deathRow, i - beg);if (modCount != expectedModCount)throw new ConcurrentModificationException();int w = beg;for (i = beg; i < end; i++)if (isClear(deathRow, i - beg))es[w++] = es[i];for (i = size = w; i < end; i++)es[i] = null;heapify();return true;}/*** @throws NullPointerException {@inheritDoc}*/public void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);final int expectedModCount = modCount;final Object[] es = queue;for (int i = 0, n = size; i < n; i++)action.accept((E) es[i]);if (expectedModCount != modCount)throw new ConcurrentModificationException();}
}

【JDK源码剖析】Queue--队列 PriorityQueue--优先队列相关推荐

  1. STL源码剖析 queue队列概述

    queue是一种先进先出的数据结构,他有两个出口 允许新增元素(从最底端 加入元素).移除元素(从最顶端删除元素),除了对于顶端和底端元素进行操作之外,没有办法可以获取queue的其他元素 即queu ...

  2. JDK源码分析实战系列-PriorityQueue

    完全二叉树 一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下.从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树 ...

  3. 横空出世。复盘B站面试坑我最深的Java并发:JDK源码剖析。B站五面面经(附过程、答案)

    上周午休我刷手机的时候看到微信在那疯狂刷恭喜,我以为发生了什么,原来是晨曦进了B站,我也刷了一句恭喜.他我印象还是比较深的,因为他给了很多我视频的建议(虽然很久没录制面试视频了),然后是比较用心那种. ...

  4. jdk包含java语言核心的类_1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  5. 我去!这也太牛了,滴滴大牛都叹服的Java并发原理,深入剖析JDK源码!

    并发编程 并发编程这四个字想必大家最近都在网上看到过有很多的帖子在讨论.我们都知道并发编程可选择的方式有多进程.多线程和多协程.在Java中,并发就是多线程模式.而多线程编程也一直是一个被广泛而深入讨 ...

  6. 跨年巨作 13万字 腾讯高工手写JDK源码笔记 带你飙向实战

    灵魂一问,我们为什么要学习JDK源码? 当然不是为了装,毕竟谁没事找事虐自己 ... 1.面试跑不掉.现在只要面试Java相关的岗位,肯定或多或少会会涉及JDK源码相关的问题. 2.弄懂原理才不慌.我 ...

  7. 【Java进阶营】膜拜 13万字 腾讯高工手写JDK源码笔记带你飙向实战

    灵魂一问,我们为什么要学习JDK源码? 当然不是为了装,毕竟谁没事找事虐自己 - 1.面试跑不掉.现在只要面试Java相关的岗位,肯定或多或少会会涉及JDK源码相关的问题. 2.弄懂原理才不慌.我们作 ...

  8. 旷世神作,腾讯高工手写13万字JDK源码笔记,从底层远吗 带你飙向实战

    灵魂一问,我们为什么要学习JDK源码? 当然不是为了装,毕竟谁没事找事虐自己 ... 1.面试跑不掉.现在只要面试Java相关的岗位,肯定或多或少会会涉及JDK源码相关的问题. 2.弄懂原理才不慌.我 ...

  9. java 头尾 队列_源码|jdk源码之栈、队列及ArrayDeque分析

    栈.队列.双端队列都是非常经典的数据结构.和链表.数组不同,这三种数据结构的抽象层次更高.它只描述了数据结构有哪些行为,而并不关心数据结构内部用何种思路.方式去组织. 本篇博文重点关注这三种数据结构在 ...

最新文章

  1. 第八周项目实践2 建立连串算法库
  2. Java 8的6个问题
  3. 自由自在休闲食品引领时尚潮流
  4. python遍历目录,获取指定文件
  5. dom4j读取xml信息
  6. PAT1130. Infix Expression (25) 中序遍历
  7. 亲密关系-【沟通提示】-如何把学习到的东西用到生活中
  8. 最新cs1.6服务器ip地址,CS1.6 IP地址大全(死亡奔跑等)
  9. 无线射频专题《IEEE 802.11协议讲解1@路由高级配置项,Beacon周期、RTS阈值、DTIM》
  10. win10专业版如何快速恢复出厂设置的方法
  11. 使用ArcMap 生成TPK和geodatabase包
  12. iOS开发者的一些前端感悟
  13. 百年工业,名词满天飞
  14. 基于STC98C52RD+的51MCU学习流水账--->串口通讯学习(汇编版)
  15. 【探索】利用 canvas 实现数据压缩
  16. BUUCTF-刷题记录-7
  17. 什么是数据科学?如何把数据变成产品?
  18. warning:iteration 7 invokes undefined behavior
  19. 国企程序员是真舒服啊,每天上班5小时,2万一月摸鱼不要太快乐
  20. 划线高亮和插入笔记的技术实现

热门文章

  1. 数据仓库Hive编程——HiveQL的数据定义(一):Hive中的数据库
  2. python_音频处理_Windows10_ raise NoBackendError() audioread.exceptions.NoBackendError
  3. Block Site
  4. 本地时间(北京时间)和“GMT+8”时区时间的区别?
  5. 记录(Record)
  6. 联想T430 安装msata接口的SSD固态硬盘
  7. EM算法 估计混合高斯模型参数 Python实现
  8. 12~18k的前端面试会问什么?
  9. 各种车辆类型的名称大全
  10. PHP获取本机真实IP