java集合类库学习记录———PriorityQueue
来源:互联网 发布:淘宝首页设计 编辑:程序博客网 时间:2024/05/29 09:20
1.特点
优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator
进行排序,具体取决于所使用的构造方法。
优先级队列不允许使用 null
元素。这是目前看到的第一个不允许放入null的集合。
优先级队列的内部结构是一个数组构成的最小堆(从0下标开始)。不了解堆数据结构的可以看数据结构与算法。
方法 iterator()
中提供的迭代器不 保证以任何特定的顺序遍历优先级队列中的元素。如果需要按顺序遍历,请考虑使用Arrays.sort(pq.toArray())
。
2.AbstractQueue
优先队列继承了AbstractQueue抽象类
AbstractQueue类提供某些 Queue
操作的骨干实现。此类中的实现适用于基本实现不 允许包含null 元素时。(实现AbstractQueue类的集合都不允许插入null元素)。
add
、remove
和 element
方法分别基于 offer
、poll
和peek
方法。
3.源码解析
PriorityQueue中有4个地方值得关注:1.构造函数 2.上浮和下沉 3.删除操作 4.迭代器
1.构造函数
public PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); }
public PriorityQueue(int initialCapacity) { this(initialCapacity, null); }
public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) { // Note: This restriction of at least one is not actually needed, // but continues for 1.5 compatibility if (initialCapacity < 1) throw new IllegalArgumentException(); this.queue = new Object[initialCapacity]; this.comparator = comparator; }
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); } }
public PriorityQueue(PriorityQueue<? extends E> c) { this.comparator = (Comparator<? super E>) c.comparator(); initFromPriorityQueue(c); }
public PriorityQueue(SortedSet<? extends E> c) { this.comparator = (Comparator<? super E>) c.comparator(); initElementsFromCollection(c); }private void initFromPriorityQueue(PriorityQueue<? extends E> c) {//利用一个优先队列初始化优先队列 if (c.getClass() == PriorityQueue.class) { this.queue = c.toArray(); this.size = c.size(); } else { initFromCollection(c); } }
private void initElementsFromCollection(Collection<? extends E> c) { //利用SortedSet初始化优先队列 Object[] a = c.toArray();//返回从小到大的顺序的数组也是符合最小堆的规则的 // If c.toArray incorrectly doesn't return Object[], copy it. if (a.getClass() != Object[].class) a = Arrays.copyOf(a, a.length, Object[].class); int len = a.length; if (len == 1 || this.comparator != null) for (int i = 0; i < len; i++) if (a[i] == null) throw new NullPointerException(); this.queue = a; this.size = a.length; }
private void initFromCollection(Collection<? extends E> c) {//其他集合初始化 initElementsFromCollection(c); heapify();//构建堆 }
private void heapify() {//从堆的前一半(高度为2的堆)开始下沉每个元素,构建最小堆 for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); }
2.上浮和下沉
private void siftUp(int k, E x) {//在指定位置k上插入元素x,进行上浮操作 if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } @SuppressWarnings("unchecked") private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) {//当x元素比父节点元素小时,上浮x到父节点 int parent = (k - 1) >>> 1; Object e = queue[parent]; if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; }
private void siftDown(int k, E x) {//在指定位置k上插入元素x,进行下沉操作 if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } @SuppressWarnings("unchecked") 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; // 默认child为左节点 Object c = queue[child]; int right = child + 1; if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) c = queue[child = right]; //当右节点存在且比左节点小时,child为右节点 if (key.compareTo((E) c) <= 0) break; queue[k] = c; //当元素x比child大时,下沉x k = child; } queue[k] = key; }
3.删除操作
public boolean remove(Object o) {//当传入参数引用和数组中的元素equals时,调用removeAt方法删除 int i = indexOf(o); if (i == -1) return false; else { removeAt(i); return true; } }
boolean removeEq(Object o) {//当传入参数引用和数组中的元素指向同一对象而非equals时,调用removeAt方法删除 for (int i = 0; i < size; i++) { if (o == queue[i]) { removeAt(i); return true; } } return false; }
private E removeAt(int i) { // assert i >= 0 && i < size; modCount++; int s = --size; if (s == i) //如果删除的是最后一个元素,直接删除 queue[i] = null; else { E moved = (E) queue[s];//存储最后一个元素 queue[s] = null;//防止对象游离 siftDown(i, moved);//下沉 if (queue[i] == moved) {//满足条件时,原最后一个元素是新子堆中最小的值 siftUp(i, moved); //上浮 if (queue[i] != moved)//如果新子堆中的最小值小于父堆时,则这个元素在迭代过程中remove操作时会被跳过,需要在迭代器中保存起来 return moved; } } return null; }
核心的removeAt方法的过程见下图:(图片来自于:http://blog.csdn.net/hxpjava1/article/details/21478323)
4.迭代器
private final class Itr implements Iterator<E> { /** * Index (into queue array) of element to be returned by * subsequent call to next. */ private int cursor = 0;//指向当前元素 /** * Index of element returned by most recent call to next, * unless that element came from the forgetMeNot list. * Set to -1 if element is deleted by a call to remove. */ private int lastRet = -1;//指向上一次next的元素(非遗漏集合中) /** * A queue of elements that were moved from the unvisited portion of * the heap into the visited portion as a result of "unlucky" element * removals during the iteration. (Unlucky element removals are those * that require a siftup instead of a siftdown.) We must visit all of * the elements in this list to complete the iteration. We do this * after we've completed the "normal" iteration. * * We expect that most iterations, even those involving removals, * will not need to store elements in this field. */ private ArrayDeque<E> forgetMeNot = null;//被遗漏元素的集合 /** * Element returned by the most recent call to next iff that * element was drawn from the forgetMeNot list. */ private E lastRetElt = null;// 被遗漏集合中上次调用next时返回的元素 /** * The modCount value that the iterator believes that the backing * Queue should have. If this expectation is violated, the iterator * has detected concurrent modification. */ private int expectedModCount = modCount; public boolean hasNext() {// 迭代操作时,当迭代完数组还要迭代被遗漏元素的集合 return cursor < size || (forgetMeNot != null && !forgetMeNot.isEmpty()); } @SuppressWarnings("unchecked") public E next() { if (expectedModCount != modCount) throw new ConcurrentModificationException(); if (cursor < size)//迭代数组 return (E) queue[lastRet = cursor++]; if (forgetMeNot != null) {//迭代被遗漏元素集合 lastRet = -1;//辅助remove方法标记 lastRetElt = forgetMeNot.poll(); if (lastRetElt != null) return lastRetElt; } throw new NoSuchElementException(); } public void remove() { if (expectedModCount != modCount) throw new ConcurrentModificationException(); if (lastRet != -1) {//当上次next使用的是非遗漏集合时,删除操作如果产生了遗漏元素则加入到遗漏集合中 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) {//当上次next使用的时遗漏集合时,在堆中删除遗漏元素(在遗漏集合中的此元素在next方法中已经删除过了) PriorityQueue.this.removeEq(lastRetElt); lastRetElt = null; } else { throw new IllegalStateException(); } expectedModCount = modCount; } }
“遗漏”元素是怎么产生的: 在迭代过程中,我们除了用迭代器的remove方法外,不能做出其他修改集合结构的操作。但是当迭代到某个元素执行迭代器删除时,会删除前一个元素(上次next返回的元素)。所以可能会产生最后一个元素上移到迭代器的上方,导致迭代器不能访问,遗漏元素就产生了。
向上图,如果迭代器在迭代到35的时候使用remove方法,则元素24就被遗漏了。所以要把它加入遗漏集合中。
- java集合类库学习记录———PriorityQueue
- Java——PriorityQueue
- JAVA学习---集合系列---PriorityQueue
- java集合类库学习记录———AbstractList
- java集合类库学习记录———ArrayList
- java集合类库学习记录———LinkedList
- 黑马程序员——Java集合框架—Queue—PriorityQueue
- java集合类深入分析之PriorityQueue
- java集合类深入分析之PriorityQueue
- java集合之PriorityQueue
- Java集合之PriorityQueue
- java集合之PriorityQueue
- java集合类库学习记录———Collection类库的结构
- java学习记录(12)——集合
- [疯狂Java]集合:Queue、PriorityQueue
- java学习PriorityQueue队列
- 优先级队列——PriorityQueue
- java集合学习记录
- 5.7大写字母与小写字母的互换与ASC||
- 2017年10月份的末尾
- 数据结构 栈
- 寻路插件A* Pathfinding Project_Pro的系统学习(一)_Pathfinder组件_扫描功能
- python学习-------数字(10/30)
- java集合类库学习记录———PriorityQueue
- 10.27周测
- 2017 ACM/ICPC 西安站 总结
- 一个关于kylin的疑惑
- 四种访问权限修饰符
- C/C++函数调用约定
- [译] AngularJS内幕详解之 Scope JAVASCRIPT Atwood.cai 2015-12-04 15617 访问 4 分享 微信分享 原文作者: Nicolas Beva
- HashMap 在JDK1.8中的实现(与JDK1.7对比)
- Netty开发的例子