【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
TaskQueue类是在Timer文件中的TaskQueue,它是一个又堆实现的优先队列。TaskQueue按任务剩下一次执行时间排序的优先级队列,使用的数据结构是小顶堆(保证堆顶的元素最小) 堆的性质并不保证有序,只是保证能在O(1)找到最小值(小顶堆)或者最大值(大顶堆)
堆本质上是一个完全二叉树,保证了根节点总是比叶子节点大或者小。因为这个优先级队列主要是由来存放TimerTask的,所以它使用的是一个TimerTask的数组来实现的。
import java.util.Arrays;
import java.util.TimerTask;
class TaskQueue {
/**
* 使用数组存为,数据是TimerTask类型
*/
private TimerTask[] queue = new TimerTask[128];
/**
* 任务队列的大小,queue[0]没有使用,queue[1]-queue[size]是排好序的
*/
private int size = 0;
int size() {
return size;
}
/**
*
*/
TimerTask getMin() {
return queue[1];
}
TimerTask get(int i) {
return queue[i];
}
/**
* 这里快速删除,并没有维护堆,可能是大神考虑到只有在TimerTask的purge中调用了改方法
* purge方法中如果删除了至少1个元素,就会调用heapify方法重新建堆
* 1 <= i <= size.
*/
void quickRemove(int i) {
assert i <= size;
queue[i] = queue[size];
queue[size--] = null; // 删除多余的引用,防止内存泄露
}
boolean isEmpty() {
return size==0;
}
/**
* 清除队列
*/
void clear() {
// 清除引用,避免内存泄露
for (int i=1; i<=size; i++)
queue[i] = null;
size = 0;
}
/**
* 添加一个新任务到队列作为叶子节点,然后从这个节点开始重新建堆
*/
void add(TimerTask task) {
// 保证数组的大小能够存放
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);
}
/**
* 自底向上重建堆,k>>1 整数k右移1位表示除以2 k>>1 ==>k/2
* 根据堆的性质可以知道一个节点k 的父节点可以表示为k/2
* 因此这个方法的从指定的节点k,依次检查和它的父节点的关系
* 如果父节点大于子节点就交换父节点与子节点(小顶堆,堆顶是最小的元素)
*/
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
/**
*自顶向下重建堆,k<<1 整数k向左移1位表示乘以2 k<<1 ==>k*2
*根据堆的性质知道,对于一个节点k,它的子节点分别为k*2 和k*2+1(如果存在)
*
*这个方法就是对于节点k如果存在子节点,则找到子节点中较小的一个和k进行比较交换
*/
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size && j > 0) {
if (j < size &&
queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
j++; // j indexes smallest kid
if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
/**
* 移除最小的(第一个),然后把最后一个元素移到堆顶,执行自顶向下重新建堆
*/
void removeMin() {
queue[1] = queue[size];
queue[size--] = null;
fixDown(1);
}
/**
* 最小元素改变,重新建堆
*/
void rescheduleMin(long newTime) {
queue[1].nextExecutionTime = newTime;
fixDown(1);
}
/**
* 重建堆,因为叶子节点满足条件了,所以从最后一个根节点开始
*/
void heapify() {
for (int i = size/2; i >= 1; i--)
fixDown(i);
}
}
其实这个类真的值得仔细阅读,代码实现的真的非常优雅。它非常巧妙的从数组下标为1的位置开始存放元素,使得fixUp方法和fixDown方法有一种赏心悦目的感觉。
在TaskQueue中并没有排序的操作,因为只需要每一次取最小的元素(堆顶的元素)就可以了,堆不会保证有序,但是保证堆顶的元素一定是最值。其实有了上面的代码写一个排序代码真的非常容易了,下面就是把数据TimerTask换为int类型的实现,其实只是写了一个堆排序的方法。
import java.util.Arrays;
public class IntQueue {
private int[] queue = new int[16];
private int size = 0;
int size() {
return size;
}
void add(int task) {
// 保证数组的大小能够存放
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);
}
/**
* 自底向上建堆
*/
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j] <= queue[k])
break;
int tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
/**
*
*/
void removeMin() {
queue[1] = queue[size];
fixDown(1);
}
/**
*自顶向下建堆
*/
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size && j > 0) {
if (j < size &&
queue[j] > queue[j+1])
j++; // j indexes smallest kid
if (queue[k] <= queue[j])
break;
int tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
/**
* 建堆
*/
void heapify() {
for (int i = size/2; i >= 1; i--)
fixDown(i);
}
@Override
public String toString() {
return Arrays.toString(queue);
}
void sort()
{
while(size>1)
{
int tmp = queue[1];
queue[1]=queue[size];
queue[size] = tmp;
size--;
fixDown(1);
}
}
public static void main(String[] args) {
IntQueue queue = new IntQueue();
int [] data = new int [10];
for(int i=0;i<10;i++)
data[i] = (int) (Math.random()*100);
for(int i=0;i<data.length;i++)
queue.add(data[i]);
System.out.println(queue.toString());
queue.sort();
System.out.println(queue.toString());
}
}
来源:oschina
链接:https://my.oschina.net/u/2474629/blog/691453