PriorityQueue介绍
在平时的编程工作中似乎很少碰到PriorityQueue(优先队列) ,故很多人一开始看到优先队列的时候还会有点迷惑。优先队列本质上就是一个最小堆。前面一篇文章介绍了堆排序和堆的性质。而堆又是什么呢?它是一个数组,不过满足一个特殊的性质。我们以一种完全二叉树的视角去看这个数组,并用二叉树的上下级关系来映射到数组上面。如果是最大堆,则二叉树的顶点是保存的最大值,最小堆则保存的最小值。
下面是一个典型的优先队列的结构图:
它的每个父节点都比两个子节点要小,但是整个数组又不是完全顺序的。
有了前面的这些铺垫,我们再来看它的整体结构和各种调整过程。
建堆
PriorityQueue内部的数组声明如下:
private transient Object[] queue; private static final int DEFAULT_INITIAL_CAPACITY = 11;
它默认的长度为11. 建堆的过程中需要添加新的元素,
PriorityQueue的建堆过程和最大堆的建堆过程基本上一样的,从有子节点的最靠后元素开始往前,每次都调用siftDown方法来调整。这个过程也叫做heapify。
private void heapify() { for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); } private void siftDown(int k, E x) { if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; int half = size >>> 1; // loop while a non-leaf while (k < half) { int child = (k << 1) + 1; // assume left child is least 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; } 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; }siftDown的过程是将一个节点和它的子节点进行比较调整,保证它比它所有的子节点都要小。这个调整的顺序是从当前节点向下,一直调整到叶节点。
siftDown的过程如下图所示:
因为一开始要建堆的时候,里面的元素是杂乱无章的,所以调整前可能的结构如下:
假设我们siftDown的元素是9,则它先和它的左右子节点进行比较,然后和最小的子节点交换位置:
经过一次交换之后,发现它还有子节点,而且子节点比它小,所以需要继续交换。
按照这个思路来理解,前面的过程就很明了了。
扩展
堆里面的数组长度不是固定不变的,如果不断往里面添加新元素的时候,也会面临数组空间不够的情形,所以也需要对数组长度进行扩展。这个扩展方法的源头就是由插入元素引起的。
数组长度扩展的方法如下:
private void grow(int minCapacity) { int oldCapacity = queue.length; // Double size if small; else grow by 50% int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1)); // overflow-conscious code if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); queue = Arrays.copyOf(queue, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
这部分代码和ArrayList的内部实现代码基本相同,都是先找到合适的数组长度,然后将元素从旧的数组拷贝到新的数组。
而添加新元素的过程如下:
public boolean offer(E e) { if (e == null) throw new NullPointerException(); modCount++; int i = size; if (i >= queue.length) grow(i + 1); size = i + 1; if (i == 0) queue[0] = e; else siftUp(i, e); return true; }
每次添加新的元素进来实际上是在数组的最后面增加。在增加这个元素的时候就有了判断数组长度和调整的一系列动作。等这些动作调整完之后就要进行siftUp方法调整。这样做是为了保证堆原来的性质。
siftUp的过程可以用如下图来描述:
假设我们在后面添加了元素3,那么我们就要将这个元素和它的父节点比较,如果它比它的父节点小,则要交换他们的顺序。这样一直重复到它大于等于它的父节点或者到根节点。
经过调整之后,则变成符合条件的最小堆:‘
它的详细实现代码如下:
private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } 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; } 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; }
注意事项:
我们看前面的调整方法不管是siftUp还是siftDown都用了两个方法,一个是用的comparator,还有一个是用的默认比较结果。这样做的目的是考虑到我们要比较的元素不仅仅是数字等类型,也有可能是被定义了可比较的数据类型。对于自定义的数据类型,他们的大小比较定义需要实现comparator接口。至于使用comparator接口的意义背后的思想,可以参照我的这一篇博文。
总结
PriorityQueue本质上就是堆排序里面建的最小堆。最小堆满足的一个基本性质是堆顶端的元素是所有元素里最小的那个。如果我们将顶端的元素去掉之后,为了保持堆的性质,需要进行调整。对堆的操作和调整主要包含三个方面,增加新的元素,删除顶端元素和建堆时保证堆性质的操作。前面讨论堆排序的文章已经有了一个详细的介绍。这里主要结合jdk的类库实现,看看一个用于实际生产环境的优先队列实现。 另外,PriorityQueue在一些经典算法中也有得到应用,相当于是它们实现的基础。
相关推荐
这个“一个讲解很清晰的Java集合框架PPT”显然是一个对外公开的教育资源,旨在帮助学习者深入理解Java集合的概念、结构以及实际应用。 在Java中,集合框架主要包括四大接口:List、Set、Queue和Map。每个接口都有...
5. **源码分析**:深入理解Java集合框架的源码,有助于优化代码性能。例如,了解HashMap的扩容机制、LinkedList的节点操作、ArrayList的动态数组管理等,能够帮助开发者在设计和实现自己的数据结构时避免常见陷阱。 ...
这一章可能详细讲解了某种特定的数据结构或Java集合框架的高级话题,如树的遍历方法(前序、中序、后序),堆(优先队列)的概念及其在Java中的实现(PriorityQueue),或者是对并发集合类如ConcurrentHashMap的分析...
在面试中,对于Java集合的深入理解往往被视为衡量开发者能力的重要指标。本文将深入剖析Java集合的源码,探讨其内部实现机制,并结合常见面试题,帮助你更好地理解和应用这些知识。 首先,我们从基础开始,Java集合...
优先队列(PriorityQueue)是Java集合框架中的一个重要组件,它提供了一种高效处理具有优先级的数据结构。在Java中,PriorityQueue类实现了Queue接口,它遵循最小堆的原理,即队首元素总是队中最小的(默认情况下)...
这篇博文链接虽然没有提供具体的内容,但是我们可以基于Java集合框架的常见知识点进行深入的解释。 首先,我们来了解一下Java集合框架的基本层次结构。在Java中,集合框架主要分为两大类:接口和实现。接口定义了...
源码阅读对于深入理解Java集合框架至关重要。通过分析`ArrayList`、`HashMap`等类的实现,我们可以了解它们如何利用数据结构优化性能,以及如何处理并发问题。例如,`ArrayList`的扩容机制,`HashMap`的哈希冲突解决...
2. **Java集合框架**: - **ArrayList与LinkedList**:它们的实现方式、增删查改性能比较,以及应用场景。 - **HashMap与HashTable**:两者的区别,线程安全问题,以及HashMap的实现原理。 - **Set与List接口**:...
**一、Java集合类** Java集合框架是处理对象组的重要工具,它包括了数组、列表、队列、集合、映射等数据结构。主要分为两大接口:`Collection`和`Map`。`Collection`接口下有`List`(有序可重复)和`Set`(无序不可...
在准备2023年的Java求职面试时,深入理解Java集合框架是至关重要的。这个集合框架是Java编程语言的核心组成部分,它提供了数据结构和算法的基础,使得开发者能够高效地存储、管理和操作对象。以下是一些关于Java集合...
本项目"Dubber-java-collection"显然是一个针对Java集合类的学习资源,特别强调了对源码的分析和理解。Dubber,通常与阿里巴巴的开源服务框架相关,这里可能是借用其名,表示这是一个开源项目,旨在帮助开发者深入...
Java集合框架是Java编程语言中的核心部分,它提供了一组高级数据...通过对`JavaCollection`源码的深入分析,我们可以更好地理解Java集合框架的工作原理,从而在实际开发中做出更明智的选择,提升代码的性能和可维护性。
本课件集合主要针对Java中的数据结构及其算法进行了深入的探讨和分析。 在"java数据结构课件与分析"中,我们可以期待学习到以下几个关键的知识点: 1. **数组**:Java中的基础数据结构,用于存储固定数量的相同...
分析这些集合类的源码有助于理解它们的工作原理,例如`HashMap`的扩容机制,`LinkedList`的插入删除效率,`TreeSet`的红黑树平衡等。 8. **性能比较与选择**: 学习如何根据应用场景选择合适的集合实现,比如对于...
泛形是Java 5引入的新特性,增强了类型安全性,允许在定义集合类时指定元素类型。反射机制允许程序在运行时检查类、接口、字段和方法的信息,甚至动态调用方法和创建对象。 枚举是一种特殊的类,常用于定义一组固定...
本项目"java_collection_source_code_analyze"专注于对Java集合框架的源代码进行深入分析,帮助开发者理解其内部机制,从而更好地利用这些工具。下面我们将详细探讨Java集合框架中的主要类、接口以及它们的实现和...
Java Collections框架是Java编程语言中一个至关重要的部分,它提供了对数据结构的高效管理,包括各种集合类,如List、Set、Queue等,以及映射类Map。在本研讨会资料中,我们将深入探讨这个框架的核心概念、设计模式...
9. **Java集合框架的高级主题**:包括并发容器(如ConcurrentHashMap)、队列(如ArrayDeque)和优先级队列(PriorityQueue),以及实用工具类(如Collections和Arrays)的使用。 10. **Java的网络编程**:讲解...
7.5.2 PriorityQueue实现类 269 7.6 Map 270 7.6.1 HashMap和Hashtable实现类 271 7.6.2 SortedMap接口和TreeMap实现类 276 7.6.3 WeakHashMap实现类 279 7.6.4 IdentityHashMap实现类 280 7.6.5 EnumMap实现...
3. **集合框架**:Java集合框架包括List(ArrayList, LinkedList, Vector)、Set(HashSet, TreeSet)、Queue(LinkedList, PriorityQueue)以及Map(HashMap, TreeMap, ConcurrentHashMap)。面试中可能会要求解释...