Timer源碼之TaskQueue

     TaskQueue類是在Timer文件中的TaskQueue,它是一個又堆實現的優先隊列。TaskQueue按任務剩下一次執行時間排序的優先級隊列,使用的數據結構是小頂堆(保證堆頂的元素最小) 堆的性質並不保證有序,只是保證能在O(1)找到最小值(小頂堆)或者最大值(大頂堆)java

    堆本質上是一個徹底二叉樹,保證了根節點老是比葉子節點大或者小。由於這個優先級隊列主要是由來存放TimerTask的,因此它使用的是一個TimerTask的數組來實現的。
 api

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());
            
    }
}
相關文章
相關標籤/搜索