Redis数据结构之IntSet

什么是IntSet

IntSet基于整数数组实现,并且长度可变,数组有序,是Set集合的一种实现方式;用来存储无重复的整数有序集合,并且可以根据元素的值,统一更改编码格式(但只能小变大);也就是整数数组存储的几个编码格式;分别是16位(int16_t)、32位 (int32_t)、64位 (int64_t);分别对应2、4、8字节的整数类型,也就是java中short、int、long整数类型;

IntSet结构

typedef struct intset {
    uint32_t encoding;  
    uint32_t length;    
    int8_t contents[];   
} intset; 

encoding便是contents数组的编码方式;分别有(INTSET_ENC_INT16、INTSET_ENCINT_32 和 INTSET_ENC_INT64)

length表示IntSet包含的元素的数量;

IntSet结构图示

一个存储了{5,15,25}的IntSet结构图示大概这样

encoding: INTSET_ENC_INT16length: 351525

头部分:8个字节  (encoding:4字节、length:4字节)

数组部分:3 * 2字节 = 6 字节

为什么编码一致

为什么编码一致?编码一致,不是会导致内存浪费吗?

其实细想就会知道,编码一致会避免很多不必要的麻烦,其中还有很重要的一点就是:编码一致之后,查找速度(寻址)就快了很多。计算指针的偏移量后就可以知道该元素的存储位置。

比如一个2字节编码数组的第三个元素,角标就是2,那么偏移量就是2*2=4个字节,从这个地址开始两个字节就是第三个元素;

所以该元素内存地址就是:数组起始地址加偏移量

如何实现长度可变和数组有序?

添加元素

intsetAdd函数添加一个元素value时,首先根据value的字节数与当前intset的encoding进行比较,分析intset是否需要升级,若需要升级则调用intsetUpdateAndAdd函数处理,否则如果value已存在intset中直接pass,不存在,那么先resize,接着将插入位置之后的所有元素向后偏移,添加value。

动态升级

当插入的元素字节数超过了原数组encoding范围时,就会调用intsetUpdateAndAdd进行升级。

假设当前存储{5,15,25}三个2字节,现要添加数49999,已经超过int16_t的范围了所以就需要进行编码升级。升级过程如下:

encoding: INTSET_ENC_INT16length: 351525
4字节4字节2字节2字节2字节

统一编码为4个字节:按照4字节大小和元素个数扩容数组,申请内存空间;

倒叙依次将原数组拷贝到扩容后正确的位置(倒叙是为了避免覆盖);

将新增元素添加到数组末尾(因为进行了编码升级,所以要么比原数组都大,要么比原数组都小);

最后更改头部encoding和length;

encoding: INTSET_ENC_INT32length: 45152549999
4字节4字节4字节4字节4字节4字节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值