前言
整數集合(intset)並不是一個基礎的數據結構,而是Redis自己設計的一種存儲結構,是集合鍵的底層實現之一,當一個集合只包含整數值元素,並且這個集合的元素數量不多時, Redis i就會使用整數集合作為集合鍵的底層實現。
一、整數集合實現
整數集合(intset)是Redis用於保存整數值的集合抽象數據結構,它可以保存類型為int16_t、int32_t或者int64_t的整數值,並且保證集合中不會出現重復元素。
//每個intset結構表示一個整數集合
typedef struct intset{
//編碼方式
uint32_t encoding;
//集合中包含的元素數量
uint32_t length;
//保存元素的數組
int8_t contents[];
} intset;
- contents數組是整數集合的底層實現,整數集合的每個元素都是 contents數組的個數組項(item),各個項在數組中按值的大小從小到大有序地排列,並且數組中不包含任何重復項。
- length屬性記錄了數組的長度。
- intset結構將contents屬性聲明為int8_t類型的數組,但實際上 contents數組並不保存任何int8t類型的值, contents數組的真正類型取決於encoding屬性的值。encoding屬性的值為INTSET_ENC_INT16則數組就是uint16_t類型,數組中的每一個元素都是int16_t類型的整數值(-32768——32767),encoding屬性的值為INTSET_ENC_INT32則數組就是uint32_t類型,數組中的每一個元素都是int16_t類型的整數值(-2147483648——2147483647)。
如上圖,為一int16_t類型的整數集合,我們可以看到數組中存儲了5個int16_t類型的整數,它們按照從小到大的順序依次排列。這個時候我們思考一個問題。如果這個時候存入一個int32_t類型的整數會怎么樣?內存溢出?這個時候就要提到整數集合的升級。
二、整數集合的升級
2.1 整數集合升級過程
正如上面所提到的問題,每當我們要將一個新元素添加到整數集合里面,並且新元素的類型比整數集合現有所有元素的類型都要長時,整數集合需要先進行升級,然后才能將新元素添加到整數集合里面。升級整數集合並添加新元素主要分三步來進行。
- 根據新元素的類型,擴展整數集合底層數組的空間大小,並為新元素分配空間。
- 將底層數組現有的所有元素都轉換成與新元素相同的類型,並將類型轉換后的元素放置到正確的位上,而且在放置元素的過程中,需要繼續維持底層數組的有序性質不變。
- 將新元素添加到底層數組里面。
2.2 整數集合升級的優點
- 提升靈活性
因為C語言是靜態類型語言,為了避免類型錯誤,我們通常不會將兩種不同類型的值放在同一個數據結構里面。
例如,我們一般只使用int16_t類型的數組來保存int16_t類型的值,只使用int32_t類型的數組來保存int32_t類型的值,諸如此類。但是,因為整數集合可以通過自動升級底層數組來適應新元素,所以我們可以隨意地將int16_t、int32_t或者int64_t類型的整數添加到集合中,而不必擔心出現類型錯誤,這種做法非常靈活。
- 節約內存
要讓一個數組可以同時保存int16_t、int32_t、int64_t三種類型的值,最簡單的做法就是直接使用int64t類型的數組作為整數集合的底層實現。不過這樣一來,即使添加到整數集合里面的都是int16_t類型或者int32_t類型的值,數組都需要使用int64_t類型的空間去保存它們,從而出現浪費內存的情況。
而整數集合現在的做法既可以讓集合能同時保存三種不同類型的值,又可以確保升級操作只會在有需要的時候進行,這可以盡量節省內存。如果我們一直只向整數集合添加int16_t類型的值,那么整數集合的底層實現就會一直是int16_t類型的數組,只有在我們要將int32_t類型或者int64_t類型的值添加到集合時,程序才會對數組進行升級。
2.3 降級
整數集合不支持降級操作,一旦對數組進行了升級,編碼就會一直保持升級后的狀態。也就是說一旦我們向一個int16_t的整數集合內添加了一個int32_t的元素后,整數集合將升級到int32_t類型。即使后續的操作中我們刪除了這個元素,整數集合還是會保持int32_t類型的狀態。
三、整數集合常用操作時間復雜度
操作 | 時間復雜度 |
---|---|
創建一個新的整數集合 | O(1) |
添加指定元素到集合 | O(N) |
移除指定元素 | O(N) |
判斷指定元素是否在集合中 | O(logN) |
隨機返回一個元素 | O(1) |
取出在指定索引上的元素 | O(1) |
返回集合包含的元素個數 | O(1) |
返回集合占用的內存字節數 | O(1) |
本文重點
- 整數集合是Redis自己設計的一種存儲結構,集合鍵的底層實現之一。
- 整數集合的底層實現為數組,這個數組以有序、無重復的方式保存集合元素,在有需要時,程序會根據新添加元素的類型,改變這個數組的類型。
- 升級操作為整數集合帶來了操作上的靈活性,並且盡可能地節約了內存。
- 整數集合只支持升級操作,不支持降級操作。
參考
《Redis設計與實現》
《Redis開發與運維》
《Redis官方文檔》