分類
發燒車訊

聊聊算法–堆的構建和調整

先提個問題,完全二叉樹/滿二叉樹,區別?前者是指每一層都是緊湊靠左排列,最後一層可能未排滿,後者是一種特殊的完全二叉樹,

每層都是滿的,即節點總數和深度滿足N=(2^n) -1。堆Heap,一堆蘋果,為了賣相好,越好看的越往上放,就是大頂堆;為了蘋果堆

的穩定,質量越小越往上放,就是小頂堆;堆首先是完全二叉樹,但只確保父節點和子節點大小邏輯,不關心左右子節點的大小關係,

通常是一個可以被看做一棵樹的數組對象,是個很常見的結構,比如BST對象,都與堆有關係,今天就說下這個重要的數據結構和應用。

 

作者原創文章,謝絕一切轉載,違者必究!

本文只發表在”公眾號”和”博客園”,其他均屬複製粘貼!如果覺得排版不清晰,請查看公眾號文章。 

 

準備:

Idea2019.03/Gradle6.0.1/Maven3.6.3/JDK11.0.4

難度: 新手–戰士–老兵–大師

目標:

1.堆的構建和調整算法

1 優先級隊列

為理解堆的原理,先看優先級隊列,它是一種數據結構,插入或者刪除元素的時候,元素會自動排序,(優先級不是狹義的數值大小,

但為了通俗理解,這裏以字母序為例),通常使用數組存儲,我們可以按照下圖進行轉換,序號 0 不用:

優先級隊列的實現(Java版):

public class PriorityQueue<Key extends Character> {
    /** 存儲元素的數組 */
    private Key[] keys;
    private int N = 0;

    public PriorityQueue(int capacity){
        // 下標0不用,多分配一個單位
        keys = (Key[]) new Character[capacity + 1];
    }

    public Key max(){
        return keys[1];
    }

    public void insert(Key e){
        N ++;
        keys[N] = e;
        swim(N);
    }
    public Key delMax(){
        Key max = keys[1];
        swap(1,N);
        keys[N] = null;
        N --;
        // 讓第一個元素下沉到合適的位置
        sink(1);
        return max;
    }
    /** 上浮第k個元素*/
    private void swim(int k){
        // 比父節點小,即進行交換,直到根
        while (k > 1 && less(parent(k),k)){
            swap(k,parent(k));
            k = parent(k);
        }
    }
    /** 下沉第 k 個元素*/
    private void sink(int k){
        while(k < N){
            int small = left(k);
            if (right(k) < N && less(right(k),left(k))){
                small = right(k);
            }
            if (less(k,small)){
                swap(k,small);
                k = small;
            }
        }
    }
    private void swap(int i,int j){
        Key temp = keys[i];
        keys[i] = keys[j];
        keys[j] = temp;
    }
    /** 元素i和j大小比較*/
    private boolean less(int i,int j){
//   'a' - 'b' = -1 ;
        return keys[i].compareTo(keys[j]) > 0;
    }
    /** 元素i的父節點*/
    private int parent(int i){
        return i/2;
    }
    /** 元素i的左子節點*/
    private int left(int i){
        return i * 2;
    }
    /** 元素i的右子節點*/
    private int right(int i){
        return i * 2 + 1;
    }
}
 

以上代碼解析:

1 swim 上浮,對於元素k,是否需要上浮,僅需與其父節點比較,大於父節點則交換,迭代直到根節點;

2 sink 下沉,對於元素k,是否需要下沉,需先比較其左右子節點,找出左右子節點中較小者,較小者若比父節點大,則交換,迭代直到末尾元素;

3 insert 插入,先將元素放到數組末尾位置,再對其進行上浮操作,直到合適位置;

4 delMax 刪除最大值,大根堆,故第一個元素最大,先將首末元素交換,再刪除末尾元素,再對首元素下沉操作,直到合適位置;

總結:以上只是Java簡化版,java.util.PriorityQueue 是JDK原版,客官可自行研究。但設計還是非常有技巧的,值得思考一番,假設 insert 插入

到首位,會導致數組大量元素移動。delMax 若直接刪除首位最大值,則需要進一步判斷左右子節點大小,並進行先子節點上浮再首元素下沉操作。

        有了這個堆結構,就可以進行堆排序了,將待排數全部加入此堆結構,然後依次取出,即成有序序列了!

2 堆排序

如要求不使用上述堆數據結構。思路(升序為例):將數組構建為一個大頂堆,首元素即為數組最大值,首尾元素交換;排除末尾元素后調整大頂堆,

則新的首元素即為次最大值,交換首尾並再排除末尾元素;如此循環,最後的數組即為升序排列

public class HeapSort02 {
    public static void main(String []args){
        int []arr = {2,1,8,6,4,7,3,0,9,5};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int []arr){
        int len = arr.length;
        // 創建一個大頂堆
        for(int i = (int) Math.ceil(len/2 - 1); i >= 0; i--){
            //從第一個非恭弘=叶 恭弘子結點從下至上,從右至左調整結構
            adjustHeap(arr,i,len);
        }
        // 交換首尾元素,並重新調整大頂堆
        for(int j = len-1;j > 0;j--){
            swap(arr,0,j);
            adjustHeap(arr,0,j);
        }
    }

    /** 迭代寫法*/
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];
        for (int k = 2*i + 1; k < length; k=k*2 + 1) {
        // 注意這裏的k + 1 < length
            // 如果右子節點大於左子節點,則比較對象為右子節點
            if (k + 1 < length && arr[k] < arr[k+1]){
                k++;
            }
            if (arr[k] > temp){
                // 不進行值交換
                arr[i] = arr[k];
                i = k;
            }
            else{
                break;
            }
        }
        arr[i] = temp;
    }

    /** 遞歸寫法*/
    private static void adjustHeap2(int[] arr, int i, int len){
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int maxIndex = i;
        // 注意這裏的 left < len
        if (left < len && arr[left] > arr[maxIndex]){
            maxIndex = left;
        }
        if (right < len && arr[right] > arr[maxIndex]){
            maxIndex = right;
        }
        if (maxIndex != i){
            swap(arr,i,maxIndex);
            adjustHeap2(arr,maxIndex,len);
        }
    }

    /** 交換元素 */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}
 

以上代碼解析:

1完全二叉樹結構中,如果根節點順序號為 0,總節點數為 N,則最末節點的父節點即為最後一個非恭弘=叶 恭弘子節點,順序號為 ceil(N/2 -1),

2 adjustHeap2 為啥使用三個參數,不用中間的參數可以?使用三個參數,是為了進行遞歸調用,因為遞歸肯定是縮小計算規模,而這裏的形參arr和len是固定不變的;

3 adjustHeap是非遞歸寫法,不用中間的參數可以?調用一在“構建大頂堆”處,可寫為函數體內初始化 i,並形成雙重 for 循環;調用二在“重新調整大頂堆”處,

    可見中間參數為 0,可直接去掉。故回答是可以!但需要調整寫法,且影響該方法復用,這裏直接寫為三個形參的函數更為優雅而已。

4非遞歸寫法理解:類似插入排序思想(依次移動並找到合適的位置再插入),先將 arr[i] 取出,然後此節點和左右子樹進行比較,如子樹更大則子節點上升一層,使

    用for循環迭代到最終位置,並進行賦值;

 

以 i=0 為例:

5遞歸方式理解:定位目標元素的左右子樹,若子樹值更大,則進行值交換,且因為子樹發生了變化,故需要對子樹進行遞歸處理;

3 前K個最大的數

在N個數中找出前K個最大的數: 思路:從N個數中取出前K個數,形成一個數組[K],將該數組調整為一個小頂堆,則可知堆頂為K個數中最小值,

然後依次將剩餘 N-K 個數與堆頂比較,若大於,則替換掉並調整堆,直到所有元素加入完畢,堆中元素即為目標集合。

public class HeapSort {
    public static void main(String[] args) {
        int[] arr = new int[100];
        for (int i = 0; i < 100; i++) {
            arr[i] = i + 1;
        }
        // 前10個最大的數
        int k = 10;
        // 構造小頂堆
        for (int i = (int) Math.ceil(k/2 - 1); i >= 0; i--) {
            adjustHeap(arr,i,k);
        }
        // 依次比較剩餘元素
        for (int i = 10; i < arr.length; i++) {
            if (arr[i] > arr[0]){
                swap(arr,0,i);
                adjustHeap(arr,0,k);
            }
        }
        // 輸出結果
        for (int i = 0; i < 10; i++) {
            System.out.print(arr[i]+"-");
        }
    }

    /** 非迭代寫法 ,對arr[i]進行調整 */
    private static void adjustHeap(int[] arr,int i,int length){
        int temp = arr[i];
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            // 因第一次循環中可能越界,故需要 k+1 < length
            if (k + 1 < length && arr[k] > arr[k + 1]){
                k++;
            }
            if (arr[k] < temp){
                arr[i] = arr[k];
                i = k;
            }
            else {
                break;
            }
        }
        arr[i] = temp;
    }
    /** 遞歸寫法 */
    private static void adjustHeap2(int[] arr,int i,int length){
        int left = i * 2 + 1;
        int right = i * 2 + 2;
        int samller = i;
        if (left < length && arr[left] > arr[samller]){
            samller = right;
        }
        if (right < length && arr[right] > arr[samller]){
            samller = right;
        }
        if (samller != i){
            swap(arr,i,samller);
            adjustHeap2(arr,samller,length);
        }
    }

    /** 交換元素 */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}
 

以上代碼解析:按照”初始化—構建小頂堆—比較調整—輸出結果”執行。注意for循環中,因第一次循環中未使用for語句條件判斷,可能越界,故需要 k+1 < length

輸出結果如下:

請看官思考,如果需求變為找出N個數中找出前K個最小的數,該如何實現? 建議動腦且動手的寫一遍!因為魔鬼在細節!

全文完!

我近期其他文章:

  • 1 Dubbo學習系列之十九(Apollo配置中心)
  • 2 聊聊算法——二分查找算法深度分析
  • 3 DevOps系列——Jenkins/Gitlab自動打包部署
  • 4 DevOps系列——Jenkins私服
  • 5 DevOps系列——Gitlab私服

    只寫原創,敬請關注 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

可拖拽圓形進度條組件(支持移動端)

好久之前寫過一個可拖拽圓形進度條的dome,中間有網友反饋過一些問題,最近比較閑有時間修改了一些問題也做了一些優化,並封裝成組件,基於canvas實現,只需傳入放置組件dom容器,任何框架均可直接使用;

codepen 示例如下:https://codepen.io/pangyongsheng/pen/XRmNRK

 

一、如何使用

npm下載

執行 npm i drag-arc -S 或 cnpm i drag-arc -S

 
import DragArc from 'drag-arc';
 new DragArc({
    el: dom,
    value: 10,
    change: (v) => {
        console.log(v)
    },
    ...
})
或者 也可從項目下載dist/dist/drag-arc.min.js,直接通過srcipt標籤引入

其中dom為放置組件HTML容器,可通過ref獲取;

主要屬性方法(詳見github/npm)

項目地址:https://github.com/pangyongsheng/canvas-arc-draw
npm地址:https://www.npmjs.com/package/drag-arc

Name Description Type Default Required
el 放置組件的DOM元素 Element none Y
change 當前值變化時觸發的事件,回調參數為當前進度值Number(0-100) Function ()=>{} N
startDeg 滑動圓弧的起始弧度 Number  0 N
endDeg 滑動圓弧的結束弧度 Number 1 N
value 默認值 Number (0-100) 0 N
textShow 显示文字 Boolean true N
color 外側圓弧顏色 String,Array [“#06dabc”, “#33aaff”] N
slider 滑塊半徑 Number #FFF N
innerColor 內側弧度的顏色 String #ccc N
outColor 外側圓弧背景顏色 String,Array #ccc N
innerLineWidth 內側弧線寬 Number 1 N
outLineWidth 外側弧線寬 Number 20 N
counterclockwise 逆時針方向 Boolean true N
sliderColor 滑塊顏色 String #CCC N
sliderBorderColor 滑塊邊框顏色 String #fff N

二、實現方法簡介

1、繪製位置幾何關係

如圖所示,以canvas畫布中心點建立坐標系,則有:

滑塊位置與弧度關係:

由圓的參數方程得出
x=rcosφ
y=rsinφ

鼠標移動位置與弧度關係:

通過事件回調參數 我們可以獲得 鼠標mousemove事件或者移動端touchmove事件的x,y坐標,可計算tan值為
tanφ = y/x;
再通過反三角函數有可得:
φ=arctan(tanφ)

以上基本的位置關係已經得出;

2、js實現中的幾個問題

(1)坐標的轉化方法

由於上述位置關係是基於中心坐標實現的,而canvas繪製坐標是以左上角為原點實現的,故需要實現兩種坐標的轉化關係;

(2)canvas弧度位置與正常弧度位置的轉化

下圖是canvas的弧度位置恰好與我們正常計算的方向是相反的,同樣需考慮弧度的轉換;

(3)Math.atan方法返回值與實際弧度的關係

由於Math.atan() 函數返回一個數值的反正切[- π/2 , π/2 ],
而實際中我們需要獲得到[0-2π]直接的值,所以在通過鼠標位置獲取弧度值時需要通過Math.atan(y/x)和xy在中心坐標的正負綜合判斷其所在象限從何獲取實際的獲取弧度值;

(4)弧度與進度條值得關係

由於鼠標移動觸發繪圖方法是較為連續的動畫效果,而進度是間隔的,
這裏我們需要實現個類似d3js中domain和range的比例關係。
這裏我們將值[0,100]對應弧度比例為[startDeg, endDeg]

(5)終點的判斷

由於鼠標移動的位置是任意的,可能導致滑塊到達終點後由於鼠標移動到了起點時,滑塊也直接從終點移動到起點,故需對起點終點做判斷,到達起點后不可再向後滑動,到達終點后不可再向前滑動;

3、詳細實現方法可以參考這篇文章

 https://www.cnblogs.com/pangys/p/6837344.html

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

多圖解釋Redis的整數集合intset升級過程

redis源碼分析系列文章

[Redis源碼系列]在Liunx安裝和常見API 

為什麼要從Redis源碼分析 

String底層實現——動態字符串SDS 

雙向鏈表都不懂,還說懂Redis?

面試官:說說Redis的Hash底層 我:……(來自閱文的面試題)

Redis的跳躍表確定不了解下

 

前言

大噶好,今天仍然是元氣滿滿的一天,拋開永遠寫不完的需求,拒絕要求賊變態的客戶,單純的學習技術,感受技術的魅力。(哈哈哈,皮一下很開森)

前面幾周我們一起看了Redis底層數據結構,如動態字符串SDS雙向鏈表Adlist字典Dict跳躍表,如果有對Redis常見的類型或底層數據結構不明白的請看上面傳送門。

今天來說下set的底層實現整數集合,如果有對set不明白的,常見的API使用這篇就不講了,看上面的傳送門哈。

整數集合概念

整數集合是Redis設計的一種底層結構,是set的底層實現,當集合中只包含整數值元素,並且這個集合元素數據不多時,會使用這種結構。但是如果不滿足剛才的條件,會使用其他結構,這邊暫時不講哈。

下圖為整數集合的實際組成,包括三個部分,分別是編碼格式encoding,包含元素數量length,保存元素的數組contents。(這邊只需要簡單看下,下面針對每個模塊詳細說明哈)

整數集合的實現

我們看下intset.h裏面關於整數集合的定義,上代碼哈:

//整數集合結構體
typedef struct intset {
    uint32_t encoding;  //編碼格式,有如下三種格式,初始值默認為INTSET_ENC_INT16
    uint32_t length;    //集合元素數量
    int8_t contents[];  //保存元素的數組,元素類型並不一定是ini8_t類型,柔性數組不佔intset結構體大小,並且數組中的元素從小到大排列。
} intset;               

#define INTSET_ENC_INT16 (sizeof(int16_t))   //16位,2個字節,表示範圍-32,768~32,767
#define INTSET_ENC_INT32 (sizeof(int32_t))   //32位,4個字節,表示範圍-2,147,483,648~2,147,483,647
#define INTSET_ENC_INT64 (sizeof(int64_t))   //64位,8個字節,表示範圍-9,223,372,036,854,775,808~9,223,372,036,854,775,807

 

 

編碼格式encoding

包括INTSET_ENC_INT16,INTSET_ENC_INT32,INTSET_ENC_INT64三種類型,其分別對應着不同的範圍,具體看上面代碼的註釋信息。

因為插入的數據的大小是不一樣的,為了盡可能的節約內存(畢竟都是錢,平時要省着點用),所以我們需要使用不同的類型來存儲數據。

集合元素數量length

記錄了保存數據contents的長度,即有多少個元素。

保存元素的數組contents

真正存儲數據的地方,數組是按照從小到大有序排序的,並且不包含任何重複項(因為set是不含重複項,所以其底層實現也是不含包含項的)。

整數集合升級過程(重點,手動標星)

上面的圖我們重新看下,編碼格式encoding為INTSET_ENC_INT16,即每個數據佔16位。長度length為4,即數組content裏面有四個元素,分別是1,2,3,4。如果我們要添加一個数字位40000,很明顯超過編碼格式為INTSET_ENC_INT16的範圍-32,768~32,767,應該是編碼格式為INTSET_ENC_INT32。那麼他是如何升級的呢,從INTSET_ENC_INT16升級到INTSET_ENC_INT32的呢?

1.了解舊的存儲格式

首先我們看下1,2,3,4這四個元素是如何存儲的。首先要知道一共有多少位,計算規則為length*編碼格式的位數,即4*16=64。所以每個元素佔用了16位。

2.確定新的編碼格式

新的元素為40000,已經超過了INTSET_ENC_INT16的範圍-32,768~32,767,所以新的編碼格式為INTSET_ENC_INT32。

3.根據新的編碼格式新增內存

上面已經說明了編碼格式為INTSET_ENC_INT32,計算規則為length*編碼格式的位數,即5*32=160。所以新增的位數為64-159。

4.根據編碼格式設置對應的值

從上面知道按照新的編碼格式,每個數據應該佔用32位,但是舊的編碼格式,每個數據佔用16位。所以我們從後面開始,每次獲取32位用來存儲數據。

這樣說太難懂了,看下圖。

首先,那最後32位,即128-159存儲40000。那麼第49-127是空着的。

接着,取空着的49-127最後的32位,即96到127這32位,用來存儲4。那麼之前4存儲的位置48-6349-127剩下的64-95這兩部分組成了一個大部分,即48-95,現在空着啦。

在接着在48-95這個大部分,再取后32位,即64-95,用來存儲3。那麼之前3存儲位置32-4748-95剩下的48-63這兩部分組成了一個大部分,即32-63,現在空着啦。

再接着,將32-63這個大部分,再取后32位,即還是32-63,用來存儲2。那麼之前2存儲位置16-31空着啦。

最後,將16-31和原來0-31合起來,存儲1。

至此,整個升級過程結束。整體來說,分為3步,確定新的編碼格式,新增需要的內存空間,從后往前調整數據。

這邊有個小問題,為啥要從后往前調整數據呢?

原因是如果從前往後,數據可能會覆蓋。也拿上面個例子來說,數據1在0-15位,數據2在16-31位,如果從前往後,我們知道新的編碼格式INTSET_ENC_INT32要求每個元素佔用32位,那麼數據1應該佔用0-31,這個時候數據2就被覆蓋了,以後就不知道數據2啦。

但是從后往前,因為後面新增了一些內存,所以不會發生覆蓋現象。

升級的優點

 節約內存

整數集合既可以讓集合保存三種不同類型的值,又可以確保升級操作只在有需要的時候進行,這樣就節省了內存。 

不支持降級

一旦對數組進行升級,編碼就會一直保存升級后的狀態。即使後面把40000刪掉了,編碼格式還是不會將會INTSET_ENC_INT16。

整數集合的源碼分析

創建一個空集合 intsetnew

這個方法比較簡單,是初始化整數集合的步驟,即下圖部分。

主要的步驟是分配內存空間,設置默認編碼格式,以及初始化數組長度length。

intset *intsetNew(void) {
    intset *is = zmalloc(sizeof(intset));//分配內存空間 
    is->encoding = intrev32ifbe(INTSET_ENC_INT16);//設置默認編碼格式INTSET_ENC_INT16 
    is->length = 0;//初始化length 
    return is;
}

添加元素並升級insetAdd流程圖(重點)

添加元素並升級insetAdd源碼分析

可以根據上面的流程圖,對照着下面的源碼分析,這邊就不寫啦哈。

//添加元素
//輸入參數*is為原整數集合
//value為要添加的元素
//*success為是否添加成功的標誌量 ,1表示成功,0表示失敗 
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    //確定要添加的元素的編碼格式 
    uint8_t valenc = _intsetValueEncoding(value);
    
    uint32_t pos;
    //如果success沒有初始值,則初始化為1 
    if (success) *success = 1;

   //如果新的編碼格式大於現在的編碼格式,則升級並添加元素 
    if (valenc > intrev32ifbe(is->encoding)) {
        //調用另一個方法 
        return intsetUpgradeAndAdd(is,value);
    } else {
        //如果編碼格式不變,則調用查詢方法 
        //輸入參數is為原整數集合 
        //value為要添加的數據
        //pos為位置 
        if (intsetSearch(is,value,&pos)) {//如果找到了,則直接返回,因為數據是不可重複的。 
            if (success) *success = 0;
            return is;
        }

        //設置length 
        is = intsetResize(is,intrev32ifbe(is->length)+1);
        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    }
    //設置數據 
    _intsetSet(is,pos,value);
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}


//#define INT8_MAX 127
//#define INT16_MAX 32767
//#define INT32_MAX 2147483647
//#define INT64_MAX 9223372036854775807LL 
static uint8_t _intsetValueEncoding(int64_t v) {
    if (v < INT32_MIN || v > INT32_MAX)
        return INTSET_ENC_INT64;
    else if (v < INT16_MIN || v > INT16_MAX)
        return INTSET_ENC_INT32;
    else
        return INTSET_ENC_INT16;
}


//根據輸入參數value的編碼格式,對整數集合is的編碼格式升級 
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
    //當前集合的編碼格式 
    uint8_t curenc = intrev32ifbe(is->encoding);
    //根據對value解析獲取新的編碼格式 
    uint8_t newenc = _intsetValueEncoding(value);
    //獲取集合元素數量 
    int length = intrev32ifbe(is->length);
    //如果要添加的數據小於0,則prepend為1,否則為0 
    int prepend = value < 0 ? 1 : 0;

   //設置集合為新的編碼格式,並根據編碼格式重新設置內存 
    is->encoding = intrev32ifbe(newenc);
    is = intsetResize(is,intrev32ifbe(is->length)+1);

    //逐步循環,直到length小於0,挨個重新設置每個值,從后往前 
    while(length--)
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));

    //如果value為負數,則放在最前面 
    if (prepend)
        _intsetSet(is,0,value);
    else//如果value為整數,設置最末尾的元素為value 
        _intsetSet(is,intrev32ifbe(is->length),value);
    //重新設置length 
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}


//找到is集合中值為value的下標,返回1,並保存在pos中,沒有找到返回0,並將pos設置為value可以插入到數組的位置
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
    int64_t cur = -1;

    //如果集合為空,那麼位置pos為0 
    if (intrev32ifbe(is->length) == 0) { 
        if (pos) *pos = 0;
        return 0;
    } else {
        //因為數據是有序集合,如果要添加的數據大於最後一個数字,那麼直接把要添加的值放在最後即可,返回最大值下標 
        if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
            if (pos) *pos = intrev32ifbe(is->length);
            return 0;
        } else if (value < _intsetGet(is,0)) { //如果這個數據小於數組下標為0的數據,即為最小值 ,返回0 
            if (pos) *pos = 0;
            return 0;
        }
    }
    //有序集合採用二分法 
    while(max >= min) {
        mid = ((unsigned int)min + (unsigned int)max) >> 1;
        cur = _intsetGet(is,mid);
        if (value > cur) {
            min = mid+1;
        } else if (value < cur) {
            max = mid-1;
        } else {
            break;
        }
    }

    //確定找到 
    if (value == cur) {
        if (pos) *pos = mid;//設置參數pos,返回1,即找到位置 
        return 1;
    } else {//如果沒找到,則min和max相鄰,隨便設置都行,並返回0 
        if (pos) *pos = min; 
        return 0;
    }
}

 

結語

該篇主要講了Redis的SET數據類型的底層實現整數集合,先從整數集合是什麼,,剖析了其主要組成部分,進而通過多幅過程圖解釋了intset是如何升級的,最後結合源碼對整數集合進行描述,如創建過程,升級過程,中間穿插例子和過程圖。

如果覺得寫得還行,麻煩給個贊,您的認可才是我寫作的動力!

如果覺得有說的不對的地方,歡迎評論指出。

好了,拜拜咯。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

大自然工程師河狸將修築堤壩 助英格蘭抗水患

摘錄自2019年11月20日中央通訊社倫敦報導

業務涵蓋歷史古蹟與鄉村管理的英國保育組織「國家信託」(National Trust)今天(20日)宣布,預定明年初在英格蘭南部兩地施放天生會修築堤壩的歐亞河狸,協助對抗水患。其中一地的計畫經理伊爾德利(Ben Eardley)指出:「河狸修築的堤壩在乾季可儲水,此外還有助降低下游暴洪、減少河岸侵蝕,攔截淤泥也可改善水質。」

河狸素有「大自然工程師」美譽,牠創造的濕地環境可供小至昆蟲、大至野禽等許多物種棲息。這些河狸將生活在有柵欄隔離林地,專家將監測棲地變化。

「國家信託」計畫於2025年前讓2萬5000公頃土地重新成為大量野生動植物的棲地。英國氣象局(Met Office)資料顯示,英格蘭北部近幾週遭逢嚴重水患,部分地區創下有紀錄以來最潮濕秋季。英格蘭光是今天早上就有18起水患警報,另有58起可能淹水警告。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

韓媒:台灣值得被納入全球氣候變遷體系

摘錄自2019年11月19日中央社報導

韓國「韓民族新聞」和英文報The Korea Times今(19日)同步刊載中華民國行政院環保署長張子敬署名的投書專文,呼籲國際社會接納台灣成為全球氣候變遷體系的一員。張子敬在分別以Taiwan: valuable partner in fighting climate change和「台灣也應當參與全球氣候變遷協約」為標題,向韓國報紙投書闡述台灣欲加入全球氣候變遷體系的立場。

專文指出,台灣整合中央相關部會工作,制訂「國家氣候變遷調適行動方案」,從災害、維生基礎設施、水資源、國土安全、海岸、能源及產業、農業、健康等8個面向,建構因應氣候變遷的韌性體制;在醫療領域上特別著重強化醫療衛生及防疫系統預防、減災、應變及復原能力,維護全民健康並優先保障弱勢住民。

另外在生態保育領域上,將維護農業生產資源及生物多樣性,加強監測與預警機制、強化天然災害救助及保險體系、整合科技提升農林漁牧產業抗逆境能力,並完善自然保護區經營管理、建構長期生態監測體系、強化物種及基因的多樣性保存與合理利用,以確保糧食安全並建構適應氣候風險的永續農業。

專文認為,台灣因政治成見被排除在國際組織之外,是相當不公平的,非但不符合氣候公約籲請所有國家對全球氣候變遷進行廣泛合作的精神,忽視巴黎協定強調「氣候正義」及呼籲各國採取氣候行動的重要性,更違背聯合國憲章宗旨,也弱化國際架構而對世界造成傷害。

專文強調,面對國際社會,台灣是負責任、肯貢獻的真誠朋友,樂於分享在環境治理制度、防災預警系統、能源效率提升技術、科技創新運用等相關領域的經驗,台灣努力希望能讓世界更美好,而台灣也真的值得被納入全球氣候變遷體系的一員。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

韓國入冬後空氣污染物八成來自中國

摘錄自2019年11月21日大紀元報導

韓、日、中三國對大氣污染物流動的共同研究顯示,韓國境內有高達三成的細懸浮污染物(PM2.5)來自中國,入冬後這個數字更飆高至八成。

據韓聯社報導,韓國環境部下屬國立環境科學院20日發布報告概要指,韓日主要城市由國內因素導致污染的比例分別為51%和55%,而中國是91%。

從國外成因來看,韓國的空氣污染物中,來自中日兩國的各占32%和2%,其餘來自朝鮮、蒙古、東南亞等地區。從韓日兩國流入中國的空氣污染物比重分別僅占2%和1%,從韓中兩國流入日本的比重分別為8%和25%。

然而,如果將時間範圍限定在12月至3月,中國的空氣污染物對韓國的影響更為嚴重。據韓國國立環境科學院的調查,今年1月11日至15日韓國空氣污染物中只有18%至31%來自國內因素,其餘69%至82%來自國外,其中中國占絕大多數。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

新宿市民測定所 發表嬰幼兒奶粉輻射檢查報告

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

【Spring】內嵌Tomcat&去Xml&調試Mvc

菜瓜:今天聽到個名詞“父子容器”,百度了一下,感覺概念有點空洞,這是什麼核武器?

水稻:你說的是SpringMvc和Spring吧,其實只是一個概念而已,用來將兩個容器做隔離,起到解耦的作用,其中子容器可以拿到父容器的bean,父容器拿不到子容器的。但是SpringBoot出來之後這個概念基本就被淡化掉,沒有太大意義,SpringBoot中只有一個容器了。

菜瓜:能不能給個demo?

水稻:可以。由於現在SpringBoot已經大行其道,Mvc你可能接觸的少,甚至沒接觸過。

  • 早些年啟動一個Mvc項目費老鼻子勁了,要配置各種Xml文件(Web.xml,spring.xml,spring-dispather.xml),然後開發完的項目要打成War包發到Tomcat容器中
  • 現在可以直接引入Tomcat包,用main方法直接調起。為了調試方便,我就演示一個Pom引入Tomcat的例子
  • ①啟動類
  • package com.vip.qc.mvc;
    
    import org.apache.catalina.Context;
    import org.apache.catalina.LifecycleException;
    import org.apache.catalina.LifecycleListener;
    import org.apache.catalina.startup.Tomcat;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    /**
     * 參考: * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
     * <p>
     * 嵌入tomcat,由Tomcat發起對Spring容器的初始化調用過程
     * <p>
     * - 啟動過程
     * * - Servlet規範,Servlet容器在啟動之後會SPI加載META-INF/services目錄下的實現類並調用其onStartup方法
     * * - Spring遵循規範實現了ServletContainerInitializer接口。該接口在執行時會收集WebApplicationInitializer接口實現類並循環調用其onStartup方法
     * * - 其中AbstractDispatcherServletInitializer
     * * * - 將spring上下文放入ContextLoaderListener監聽器,該監聽會發起對refresh方法的調用
     * * * - 註冊dispatcherServlet,後續會由tomcat調用HttpServletBean的init方法,完成子容器的refresh調用
     * *
     *
     * @author QuCheng on 2020/6/28.
     */
    public class SpringWebStart {
    
        public static void main(String[] args) {
            Tomcat tomcat = new Tomcat();
            try {
                // 此處需要取一個目錄
                Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
                context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
                tomcat.setPort(8081);
                tomcat.start();
                tomcat.getServer().await();
            } catch (LifecycleException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    
    
        static class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
            private final static String PACKAGE_PATH = "com.vip.qc.mvc";
    
            @Override
            protected String[] getServletMappings() {
                return new String[]{"/"};
            }
    
            @Override
            protected Class<?>[] getRootConfigClasses() {
                // spring 父容器
                return new Class[]{AppConfig.class};
            }
    
            @Override
            protected Class<?>[] getServletConfigClasses() {
                // servlet 子容器
                return new Class[]{ServletConfig.class};
            }
    
            @ComponentScan(value = PACKAGE_PATH,
                    excludeFilters = {
                            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
                            // 避免掃描到加了註解(@Configuration)的子容器
                            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ServletConfig.class)})
            static class AppConfig {
            }
    
            @ComponentScan(value = PACKAGE_PATH)
            static class ServletConfig {
            }
        }
    }
  • ②Controller&Service
  • package com.vip.qc.mvc.controller;
    
    import com.vip.qc.mvc.service.ServiceChild;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Controller
    public class ControllerT implements ApplicationContextAware {
    
        @Resource
        private ServiceChild child;
    
        @RequestMapping("/hello")
        @ResponseBody
        public String containter() {
            child.getParent();
            System.out.println("parentContainer");
            return "containter";
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("子容器" + applicationContext);
            System.out.println("子容器中獲取父容器bean" + applicationContext.getBean(ServiceChild.class));
        }
    }
    
    
    package com.vip.qc.mvc.service;
    
    import com.vip.qc.mvc.controller.ControllerT;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Service;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Service
    public class ServiceChild implements ApplicationContextAware {
    
        //    @Resource
        private ControllerT controllerT;
    
        public void getParent() {
    
            System.out.println(controllerT);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("父容器" + applicationContext);
            try {
                System.out.println("父容器中獲取子容器bean" + applicationContext.getBean(ControllerT.class));
            } catch (NoSuchBeanDefinitionException e) {
                System.out.println("找不到子容器的bean");
            }
        }
    }

    // 調用
    SpringWebStart的main方法啟動-會有如下打印
    父容器Root WebApplicationContext, started on Sun Jun 28 22:03:52 CST 2020
    找不到子容器的bean
    子容器WebApplicationContext for namespace 'dispatcher-servlet', started on Sun Jun 28 22:03:58 CST 2020, parent: Root WebApplicationContext
    子容器中獲取父容器beancom.vip.qc.mvc.service.ServiceChild@4acfc43a
    
    
  • Demo比較簡單,不過也能反映父子容器的關係

菜瓜:嗯,效果看到了,能不能講一下啟動過程

水稻:稍等,我去下載源碼。上面代碼演示中已經提前說明了,父子容器的加載是Tomcat依據Servlet規範發起調用完成的

  • spring-web源碼包的/META-INF中能找到SPI的實際加載類SpringServletContainerInitializer#onStartup()方法會搜集實現WebApplicationInitializer接口的類,並調用其onStartup方法
  • 上面MyWebApplicationInitializer啟動類是WebApplicationInitializer的子類,未實現onStartup,實際調用的是其抽象父類AbstractDispatcherServletInitializer的方法。跟進去 
  • @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
       //① 創建Spring父容器上下文-對象放入ContextLoadListener,後續調起完成初始化,
       super.onStartup(servletContext);
       //② 創建DispatcherServlet對象,後續會由tomcat調用其init方法,完成子容器的初始化工作
       registerDispatcherServlet(servletContext);
    }
    
    // ①進來
    protected void registerContextLoaderListener(ServletContext servletContext) {
        // 此處會回調我們啟動類的getRootConfigClasses()方法 - 父容器配置
        WebApplicationContext rootAppContext = createRootApplicationContext();
        if (rootAppContext != null) {
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            istener.setContextInitializers(getRootApplicationContextInitializers());
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                  "createRootApplicationContext() did not return an application context");
        }
    }
    
    // ②進來
    protected void registerDispatcherServlet(ServletContext servletContext) {
            。。。
        // 此處會回調我們啟動類的getServletConfigClasses()方法 - 子容器配置
        WebApplicationContext servletAppContext = createServletApplicationContext();
            。。。
        // 初始化的dispatcherServlet,會加入Tomcat容器中-後續調用
        // FrameworkServlet#initServletBean()會完成上下文初始化工作
        FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            。。。
    }

菜瓜:這樣容器就可以用了嗎?

水稻:是的,這樣就可以直接在瀏覽器上面訪問http://localhost:8081/hello,不過這是一個最簡陋的web項目

菜瓜:懂了,最簡陋是什麼意思

水稻:如果我們想加一些常見的Web功能,譬如說攔截器,過濾器啥的。可以通過@EnableWebMvc註解自定義一些功能

  • package com.vip.qc.mvc;
    
    import com.vip.qc.mvc.interceptor.MyInterceptor1;
    import com.vip.qc.mvc.interceptor.MyInterceptor2;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    @EnableWebMvc
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Resource
        private MyInterceptor1 interceptor1;
        @Resource
        private MyInterceptor2 interceptor2;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(interceptor1).addPathPatterns("/interceptor/**");
            registry.addInterceptor(interceptor2).addPathPatterns("/interceptor/**");
        }
    }
    
    
    
    package com.vip.qc.mvc.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    public class MyInterceptor1 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("嘻嘻 我是攔截器1 pre");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("嘻嘻 我是攔截器1 post");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("嘻嘻 我是攔截器1 after");
        }
    }
    
    
    package com.vip.qc.mvc.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    public class MyInterceptor2 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("嘻嘻 我是攔截器2 pre");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("嘻嘻 我是攔截器2 post");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("嘻嘻 我是攔截器2 after");
        }
    
    } 

菜瓜:我知道,這裏還有個Mvc請求調用流程和這個攔截器有關。而且這個攔截器不是MethodInterceptor(切面)

水稻:沒錯,說到這裏順便複習一下Mvc的請求過程

  • 請求最開始都是通過Tomcat容器轉發過來的,調用鏈:HttpServlet#service() -> FrameworkServlet#processRequest() -> DispatcherServlet#doDispather()
  •  1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     2     。。。
     3   processedRequest = checkMultipart(request);
     4   multipartRequestParsed = (processedRequest != request);
     5   // 1.返回一個持有methodHandler(按照URL匹配得出的被調用bean對象以及目標方法)調用鏈(攔截器鏈)對象
     6   mappedHandler = getHandler(processedRequest);
     7   。。。
     8   // 2.按照我們現在寫代碼的方式,只會用到HandlerMethod,其他三種基本不會用
     9   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    10   。。。
    11   // 3.前置過濾器 - 順序調用
    12   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    13       return;
    14   }
    15   // 4.Actually invoke the handler.
    16   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    17   。。。
    18     applyDefaultViewName(processedRequest, mv);
    19   // 5.後置過濾器 - 逆序調用
    20   mappedHandler.applyPostHandle(processedRequest, response, mv);
    21   。。。
    22   // 6.處理試圖 - 內部render
    23   processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    24   }
    25   catch (Exception ex) {
    26      // 異常處理
    27      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    28   }
    29       // 異常處理
    30   catch (Throwable err) {
    31      triggerAfterCompletion(processedRequest, response, mappedHandler,
    32                     new NestedServletException("Handler processing failed", err));
    33   }
    34     。。。

     

 菜瓜:這個之前看過不少,百度一大堆,不過還是源碼親切

 

總結:

  • 目前基本互聯網項目都是SpringBoot起手了,再難遇到SpringMvc的項目,不過熟悉該流程有利於我們更加深刻的理解Ioc容器
  • Mvc攔截器鏈也是日常開發中會用到的功能,順便熟悉一下請求的執行過程

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

四周年:聊聊測試工程師的核心競爭力

寫博客四周年,習慣每年這個時候做一次總結回顧。這次,就聊聊工作幾年以來,我個人對核心競爭力的一些思考和認知。。。

往期傳送門

一周年:聊聊寫博客這件事

兩周年:聊聊這一年的成長

三周年:聊聊近期目標和計劃

 

核心競爭力,各人有各人的想法。且在不同階段、不同企業、不同時期有不同的視角,無法一一而足。以我個人角度來說:核心競爭力實際上就是個人價值訴求的一種表達

我劃分了四個較為通用的維度,從這幾個維度出發,聊聊不同階段我的一些看法和建議。

思維導圖

 

時間維度

1、小白入門

通常來說,小白入門階段,指的是初入職場一年以內的同學。

在這個階段,個人的核心競爭力,或者說個人價值的體現,就是高效執行能力+快速學習能力

工作職責、範圍、流程、規範、溝通能力,在這個階段,也許會遇到不同的挑戰,但企業一般對新同學包容性都比較強,這個階段試錯空間較大,最利於新入成長。

2、快速成長

快速成長階段,一般指的是工作1-3年的同學。這個階段,個人認為核心競爭力主要體現在協同配合+ownership(主人翁意識,或者說是主動承擔工作的能力)。

當然,這個階段也會面臨職場第一次跳槽,如何編寫簡歷、面試、談offer、如何選擇就職行業、在團隊內刷存在感等事情。

表述可能有些直接,但這個階段,對未來的職場發展,影響往往是很大的。建議找個業內熟識的前輩,聊聊天,請教一下

3、專註沉澱

這個階段的同學我劃分為3-5年的同學,一般來說這個階段,會成為團隊里的中堅力量,甚至會承擔一部分所謂的“管理”的工作。

除了上述的幾點核心能力,我認為這個階段的同學,核心競爭力,往往體現在客觀的思考能力+創造價值的能力

如何理解創造價值?初入職場大多是聽安排做事,這個階段,會漸漸過渡到分配任務並且帶領小團隊快速高效完成工作的狀態。

至於專註沉澱,這個階段建議對自己的技術棧+未來五年職場規劃進行多次深入的review,這是一個不斷否定不斷堅持的過程。

4、成為專家

如何理解專家?即在某一個領域內擁有豐富經驗+專業特長+深入研究的人。一般在職場中,七年左右可以稱之為專家。

在《刻意練習:如何從新手到大師》這本書中,講述了很多關於成為某一個領域專家的方法,推薦大家閱讀這本書。

在這個階段,核心競爭力,反而簡單,即上面講到的創造價值

如何創造價值?解決團隊目前存在的核心問題,從基礎建設、流程規範、技術、分享、管理多方面來保質提效。

當然,最關鍵的是,主動傳達自己創造的價值!這個很考驗一個人的耐心和協調溝通能力,概括一下就是:深諳職場生存法則

 

核心能力

關於核心能力,其實在上面的幾個階段,都做了解讀。這裏我有個建議,你的核心能力,可以在簡歷的個人評價里體現出來

好的簡歷可以擺平面試求職路上的很多客觀因素,當然,也能吸引到某些獵頭的挖掘。

當然,不僅僅是簡歷,還有其他的一些客觀條件,這個請看下文。

1 # 這裏僅提供一個demo供參考
2 1、對項目整體業務和系統架構有較全面了解,能獨立解決複雜任務; 3 2、能與其他團隊建立良好的合作關係,並組織主導完成工作; 4 3、自我驅動能力強,熱愛分享,善於思考總結; 5 4、保持技術前瞻性,鑽研新技術及方法,並推動在工作中落地;

 

職場生涯

關於職場生涯,有很多博主很多大牛說過,這裏我順帶談談我的看法。

在專註沉澱階段(3-5年),為什麼要建議對自己的技術棧以及未來五年的職場規劃進行多次review呢?

一方面,這個階段會在技術和業務上有一定的深入和廣度;另一方面,職場或者說未來職業發展需要的是一專多能的在細小領域有較高成就的人

一般來講,這個階段需要對業務&技術做個權重。業務專家?技術大神?或者基層管理。

管理是一個蘿蔔一個坑,在互聯網領域,個人覺得對普通人來講,業務專家或技術大神,對普通人更友好。

不需要做多大範圍的TOP1,你只需要堅持去做下面幾點,就能有所收穫。

 

個人成長

關於個人成長,前段時間看了本書:《軟技能:代碼之外的生存指南》。裏面介紹了很多有趣的觀點和方法,大家不妨一讀。

挑幾個關鍵的point來談談我自己的個人成長曆程,也許會對你有所觸動。

1、寫作博客

大概工作第三年開始,我開通了博客,並且堅持每周都更新。有總結,有實踐,有思考,也有很多自己創造的內容。我把寫博客這件事叫做爆肝。。。

寫了一段時間,你會發現漸漸有了流量,有了評論,有了錯誤指摘,也有抬杠。從這些“收穫”里,你能將自己的底子打得更好,當然,知名度,或許有些罷。。。

2、開公開課

有了寫博客的沉澱(不一定寫博客,自己做筆記也可,但我還是建議寫博客,交流更重要,閉門造車要不得),你可以嘗試開公開課,比如:QQ群直播、網易公開課、騰訊課堂等。

如何將自己知道的東西,簡潔明了的傳遞給別人,在溝通分享中重塑自身的知識體系,是很有意思也很有挑戰的一件事。

職場向上發展,分享、培訓、演講是少不了的。有備無患,才是智者取勝之道。

這個過程里,你會發現自己以往的遺漏、錯誤、表述的誤差,也能從別人口中獲悉成長的力量。

3、著書立說

這個對很多同學來說可能有點不可思議,但其實沒那麼難。我認識測試圈子的同學比如小坦克,基於抓包進行接口&自動化測試,已經出了2本書了。

我自己也正在寫自己的第一本書:從零開始性能測試。

當然,給了我勇氣讓我開始下筆寫書的原因,一方面是想建立個人的品牌影響力;另一方面,通過博客,有好幾位出版社的編輯找到了我,不好意思拒絕他們的熱情相邀,就索性寫一本書吧。

人這一生,總要做幾件讓自己回想起來就覺得中二熱血的事情。。。

4、諮詢培訓

關於諮詢培訓,其實和轉崗斜杠很類似。生存所需嘛,憑自己的本事站着吃飯,不寒磣。

通過自我成長,寫博客,開公開課,建立個人影響力,嘗試付費諮詢,做培訓,運營公眾號,接商業推廣,甚至極客時間開一門課,都是可以嘗試的。

很多事情不是看到希望才做,而是做了才能有所收穫。

當然,這個過程中,你會開始思考如何拓展推廣渠道,引流拉新,提高創作內容的質量,遇到很多未知領域的挑戰。

別怕,向前走,前面有光!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

江蘇省消保委多措並舉 助力消費者提升免“疫”力

  中國消費者報報道史曄 記者薛慶元)眾志成城,共克時艱。一場來勢洶洶的新冠肺炎疫情牽動着每個消費者的心。面對嚴峻的疫情,江蘇省消費者權益保護委員會堅守“疫”線,及時開展消費宣傳引導,努力化解消費糾紛,督促經營者履行責任,儘力護衛消費者權益,用心守護消費環境穩定和民生消費安全。

  受理消費投訴5548件

  疫情期間,口罩一夜之間成了“搶手貨”。據了解,1月24日至2月9日,江蘇全省消保委系統共計受理消費維權投訴5548件,投訴類型分析,商品類投訴3468件,服務類2080件,分別佔比62.51%、37.49%。受疫情影響,線上機票、酒店、旅遊行程退訂,線下口罩、消毒防護用品購買,餐飲退訂等成為消費熱點,網購產品發貨、售後也成關注點。其中,口罩、消毒液、手套等衛生防護用品需求和投訴激增,口罩類投訴1965件,佔總投訴量35.42%,主要問題為發貨延遲、價格上漲、假冒偽劣等方面。

  2月5日,泰興市消費者錢先生通過微信購買口罩50隻,后發現所購口罩無廠名、廠址、合格證,遂向當地消費者組織進行投訴維權。泰興市消費者協會接訴后,詳細向售賣人了解口罩進貨渠道及相關程序手續,售賣人均無法回答,為防止春節期間劣質口罩流入市場,工作人員現場調解售賣人為消費者辦理退款並進行賠償,同時將售賣人基本信息和售賣情況向執法部門書面發出“行政處罰建議函”。

  發出倡議共抗疫情

  面對疫情蔓延的嚴峻形勢和投訴激增的趨勢,江蘇省消保委堅決做到“有訴必接、速查速回”,全力維護消費者權益。針對涉嫌違法的藥店、生產銷售各類劣質口罩、哄抬物價等行為,江蘇省各市消保委組織紛紛發出相關倡議,呼籲經營者尊重市場規律前提下,自覺履行法定義務,助力消費者提升“免疫力”,最大限度保障消費者權益。

  非常時期,人民齊心抗疫。南京市消協系統自除夕起安排了專人值班,全市消協系統(含分會)共有276人在崗工作,全市消協系統受理投訴主要集中在防疫用品的質量和價格、物流問題、旅遊產品等方面;連雲港市消保委成立“疫情期間消費投訴工作應急小組”,執行涉疫投訴“日報告”制度,要求各縣區及時上報涉疫消費投訴動態及處理結果,建立旅遊、商超企業等微信工作群,指導企業依法依規积極快速處理好涉疫投訴,與有關部門密切配合,對農貿市場、藥店和商超進行排查,嚴控商品價格,嚴處哄抬物價…….

  阻擊、保障兩手抓

  奮力戰“疫”,江蘇省各級消保委組織保障民生實招頻出。南通市各級消協工作人員積极參与市場監管部門對轄區內農貿市場、活禽銷售點、大小型餐飲、藥店開的防疫檢查,避免群體聚集,堅決防止疫情蔓延;鎮江丹陽市通過發放《價格政策提醒函》、《禁止野生動物交易告知書》、《餐飲行業防控措施告知書》等,加強法規宣傳和政策引導,督促經營者自律; 無錫市消保委全體人員參与農貿市場巡查督導工作……

  今年年初,楊女士從境外旅遊回南京,回程前2天偶然得知其乘坐聯程航班兩段均被取消,其本人在航司官網、預定頁面均未發現訂單更改信息,也未收到任何通知及後續措施,撥打客服電話显示無法接通狀態,聯繫無果下投訴至消保委。當地消保委接訴后與航空公司聯繫,及時安排工作人員與消費者聯繫,為消費者更改航班,保障了消費者順利返程。

  此外,江蘇省消保委牢牢守住百姓“菜籃子”,揭穿各種謠言,真切做到抓好民生保障,又嚴格做好疫情防控,保障市場秩序穩定有序,築起疫情防控銅牆鐵壁。

  生命重於泰山。江蘇省消保委深刻認識做好疫情防控的重要性和緊迫性,圍繞群眾消費熱點,咬定戰“疫”不鬆勁,多措並舉解民憂、排民難,以對人民高度負責的態度,把疫情防控工作作為當前最重要的工作來抓,全力以赴打好打贏疫情防控這場阻擊戰。

責任編輯:邊靜

本站聲明:網站內容來源再生能源資訊網http://www.ccn.com.cn/,如有侵權請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!