本文内容均来自《Redis设计与实现》一书
整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。
1.定义
结构
typedef struct intset {
// 编码方式
uint32_t encoding;
// 集合包含的元素数量
uint32_t length;
// 保存元素的数组
int8_t contents[];
} intset;
contents:是一个数组,是整数集合的底层实现,整数集合的每个元素都是contents数组的一个数组项,各个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复项。
length:记录整数集合包含的元素数量,也即是contents数组的长度。
encoding:决定contents数组真正的类型。
2.整数集合升级
每当我们要将一个新元素添加到整数集合里面,并且新元素的的类型比整数集合现在所有元素的类型都要长时,整数集合需要先进行升级,然后才能将新元素添加到集合里面。
整数集合升级的步骤
- 根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配在空间。
- 将底层数组现有的所有元素都转换成与新元素相同的类型,并将类型转换后的元素放置到正确的位上,而且放置元素的过程中,需要维持底层数组的有序性质不变。
- 将新元素添加到底层数组里面。
升级之后新元素的摆放位置
因为引发升级的新元素的长度总是比整数集合现有所有元素的长度都大,所以这个新元素的值要么大于所有现有元素(正数),要么小于所有现有元素(负数)。在新元素小于所有现有元素的情况下,新元素将会被放置在底层数组的最开头;在新元素都大于所有现有元素的情况下,新元素会被放置在底层数组的最末尾。
好处
提升灵活性:因为C语言是静态类型语言,为了避免类型错误,我们通常不会将两种不同类型的值放在同一个数据结构里面;但是,因为整数集合可以通过自动升级底层数组来适应新的元素,所以我们可以随意地将不同类型地整数添加到整数集合中,而不必担心类型错误。
节约内存:如果需要让一个数组同时保存int16_t、int32_t、int64_t类型地值,最简单地做法就是直接使用int64_t 类型的数组作为整数集合的底层实现。不过这样的话,对于整数集合都是int16_t或者int32_t类型的值时,数组都需要用int64_t类型的空间去保存它们,从而浪费内存。而整数集合的做法既可以让集合同时保存不同类型的值,又可以确保升级操作只会在有需要的时候进行,这样可以节省内存。
整数集合不支持降级操作,一旦对数组进行了升级,编码就会一直保持升级后状态。