Timer源码之TaskQueue

依然范特西╮ 提交于 2020-01-07 04:49:30

【推荐】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());
            
    }
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!