整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且数量不多时,Redis采用整数集合作为集合键的底层实现。
整数集合,可以保存int16_t、int32_t或者int64_t的整数值,且元素不重复,intset.h/intset结构表示一个整数集合:
typedef struct intset {
uint32_t encoding; // 决定contents保存的真正类型
uint32_t length;
int8_t contents[]; // 各项从小到大排序
} inset;
上图中,contents数组的大小为sizeof(int16_t) * 5 = 80位。
每当添加一个新元素到整数集合中,且新元素的类型比现有所有元素的类型都要长时,整数集合需要先升级(update),然后才能添加新元素:
- 根据新元素的类型,扩展底层数组的空间大小,并未新元素分配空间。
- 将底层数组现有元素转换成与新元素相同的类型,并放置在正确的位置上(从后向前遍历)。放置过程中,维持底层数组的有序性质不变。
- 将新元素添加到底层数组里。
因为每次升级都可能对所有元素进行类型转换,所以复杂度为O(N)。
PS. 因为引发升级的新元素长度比当前元素都大,所以它的值要么大于当前所有元素,要么就小于。前种情况放置在底层数组的末尾,后种情况放置在头部。
升级有两个好处
-
提升整数集合的灵活性
我们可以随意地将int16_t、int32_t添加到集合中,不必担心出现类型错误,毕竟C是个静态语言。
-
尽可能解约内存
避免用一个int64_t的数组包含所有元素
整数集合不支持降级。
函数 | 作用 | 时间复杂度 |
---|---|---|
intsetNew | 创建一个新的整数集合 | O(1) |
intsetAdd | 添加指定元素 | O(N) |
intsetRemove | 移除指定元素 | O(N) |
intsetFind | 检查给定值是否存在 | 因为底层数组有序,所以O(logN) |
insetRandom | 随机返回一个元素 | O(1) |
intsetGet | 返回给定索引上的元素 | O(1) |
intsetLen | 返回元素个数 | O(1) |
intsetBlobLen | 返回占用的内存字节数 | O(1) |
上一章:5. 跳跃表
下一章:7. 压缩列表