整数集合(intset) 是集合键的底层实现之一、当一个集合只包含整数值元素, 且集合元素数量不多时、Redis就会只使用整数集合作为集合键的底层实现.
整数集合intset
是Redis用于保存整数值的集合抽象数据结构, 可以保存 int16_t
, int32_t
或者 int64_t
的整数值, 且保证集合中不会出现重复元素. 每个 intset.h/int64_t
的结构表示一个整数集合
typedef struct intset {
uint32_t encoding; // 编码方式
uint32_t lenght; // 集合中包含的元素数量
int8_t contents[]; // 保存元素的数组
} intset;
contents数组
是整数集合的底层实现, 整数集合的元素在数组中按值的大小从小到大有序排列, 且数组中不包含重复项
length
记录了整数集合包含的元素数量、即 contents 数组
的长度
虽然intset将 contents 属性声明为 int8_t
类型的数组、但实际上并不保存任何 int8_t
类型的值、实际类型取决于 encoding
属性
eg. 若 encoding
的属性值为 INTSET_ENC_INT16
, 那么contents就是一个 int16_t 类型(-32768 ~ 32768)的数组.
升级
将新元素添加到整数集合、且新元素的类型比整数集合现在所有元素的类型都要长时、整数集合需要先升级, 然后才能添加新元素, 具体步骤如下:
-
根据新元素的类型、扩展整数集合底层数组的空间大小、并为新元素分配空间
-
将底层数组现有的所有元素转换成新元素相同的类型、并将类型转换后的元素放置到正确的位置上、且在放置元素的过程中、需要保持底层数据的有序性
-
将新元素添加到底层数组里
因为数据的插入都可能引起类型升级、所以插入的时间复杂度是 O(n)
升级之后元素的摆放
因为引发升级的新元素的长度总是比整数集合现有所有元素的长度都大、所以、要么值大于现在所有元素、要么就小于现在所有元素(负数)
- 在新元素小于现在所有元素的情况下、新元素插入到表头(需要搬移原所有元素)
- 新元素大于现在所有元素、插入到表尾
升级的好处
提升灵活性
C语言是静态语言、为了避免类型错误、通常不会将两种不同类型的值放在同一个数据结构里, eg. 一般只用 int16_t
类型的数组保存 int16_t
类型的值, 只使用 int32_t
类型的数组保存 int32_t
类型的值, 但是因为整数集合可以通过自动升级底层数据来适应新的元素, 可以随意将 int16_t
、int32_t
、int64_t
类型的值添加到整数集合、不必担心类型错误, 非常灵活
节约内存
要让一个数组同时保存 int16_t
, int32_t
, int64_t
类型的值、最简单的做法是直接使用 int64_t
类型的数组作为底层实现、但这样的话、即使元素全部为 int16_t
类型的值、空间也需要同样多, 浪费一部分内存
现在的设计、可以让集合同时保存三种不同类型的值、又可以确保升级操作只在必要时进行、尽可能的节省内存.
降级
整数集合不支持降级操作、一旦对数组进行了升级、编码会一直保持升级后的状态.
重点回顾
- 整数集合是集合键的底层实现之一
- 整数集合的底层实现为数组、以有序、无重复的方式保持集合元素、在有需要时根据新添加元素的类型、升级数组类型
- 整数集合只支持升级操作、不支持降级操作
网友评论