Redis之intset数据结构
2015-10-28 21:48
423 查看
0.前言
redis中intset是一个整数集合, 只能存储整数类型的数据, 可以是16位, 32位, 或者是64位, 是以升序排列的数组进行保存数据,下面会介绍具体数据结构和对其操作过程.1.数据结构定义
typedef struct intset { /*编码*/ uint32_t encoding; /*长度*/ uint32_t length; /*集合内容,按升序排列数组*/ int8_t contents[]; } intset;
2.创建集合
创建集合需要分配下内存空间, 初始化结构体内变量intset *intsetNew(void) { intset *is = zmalloc(sizeof(intset)); is->encoding = intrev32ifbe(INTSET_ENC_INT16); is->length = 0; return is; }
3.添加元素
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) { /*为了节省空间, 判断添加的元素需要编码为何种数据类型, 比如int16, int32, int64*/ uint8_t valenc = _intsetValueEncoding(value); uint32_t pos; if (success) *success = 1; /*如果intset编码位数无法容纳新元素,则需要重新更新整个intset编码*/ if (valenc > intrev32ifbe(is->encoding)) { /* 更新编码并添加新元素 */ return intsetUpgradeAndAdd(is,value); } else { /*搜索新添加元素是否已经存在,存在则返回失败,此函数在查找一节会详细讲解*/ if (intsetSearch(is,value,&pos)) { if (success) *success = 0; return is; } /*扩展内存空间*/ is = intsetResize(is,intrev32ifbe(is->length)+1); if (pos < intrev32ifbe(is->length)) /*如果添加元素位置不是一整块内存尾部,则需将其后面元素后移一个元素位置*/ intsetMoveTail(is,pos,pos+1); } /*pos位置处赋值*/ _intsetSet(is,pos,value); is->length = intrev32ifbe(intrev32ifbe(is->length)+1); return is; } /*根据元素大小决定元素存储长度*/ static uint8_t _intsetValueEncoding(int64_t v) { if (v < INT32_MIN || v > INT32_MAX) return INTSET_ENC_INT64; else if (v < INT16_MIN || v > INT16_MAX) return INTSET_ENC_INT32; else return INTSET_ENC_INT16; } /*重置intset空间大小,每次zrealloc扩展内存大小*/ static intset *intsetResize(intset *is, uint32_t len) { uint32_t size = len*intrev32ifbe(is->encoding); is = zrealloc(is,sizeof(intset)+size); return is; } /*向后移动元素*/ static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) { void *src, *dst; uint32_t bytes = intrev32ifbe(is->length)-from; uint32_t encoding = intrev32ifbe(is->encoding); if (encoding == INTSET_ENC_INT64) { src = (int64_t*)is->contents+from; dst = (int64_t*)is->contents+to; bytes *= sizeof(int64_t); } else if (encoding == INTSET_ENC_INT32) { src = (int32_t*)is->contents+from; dst = (int32_t*)is->contents+to; bytes *= sizeof(int32_t); } else { src = (int16_t*)is->contents+from; dst = (int16_t*)is->contents+to; bytes *= sizeof(int16_t); } memmove(dst,src,bytes); } /* 更新集合编码并添加新元素 */ static intset *intsetUpgradeAndAdd(intset *is, int64_t value) { uint8_t curenc = intrev32ifbe(is->encoding); uint8_t newenc = _intsetValueEncoding(value); int length = intrev32ifbe(is->length); int prepend = value < 0 ? 1 : 0; /* 设置新编码,并扩展足够内存空间*/ is->encoding = intrev32ifbe(newenc); is = intsetResize(is,intrev32ifbe(is->length)+1); /* 取出原来空间中元素,从后开始往前依次放入新的位置 */ while(length--) _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc)); /* 放置value值,要么在数组头,要么在数组尾部 */ if (prepend) _intsetSet(is,0,value); else _intsetSet(is,intrev32ifbe(is->length),value); is->length = intrev32ifbe(intrev32ifbe(is->length)+1); return is; }
4.查找元素
查找元素依靠intsetFind函数进行,调用intsetSearch进行实际查找uint8_t intsetFind(intset *is, int64_t value) { /*判断待查元素编码是否符合条件,不符合直接返回false,否则进入intsetSearch进行实际查找*/ uint8_t valenc = _intsetValueEncoding(value); return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL); } static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) { int min = 0, max = intrev32ifbe(is->length)-1, mid = -1; int64_t cur = -1; /* 集合为空,直接返回第一个位置 */ if (intrev32ifbe(is->length) == 0) { if (pos) *pos = 0; return 0; } else { /* _intsetGet函数仅仅获取set集合中pos位置的值, 如果待查元素大于集合尾部元素,则直接返回待查元素位置为集合长度*/ if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) { if (pos) *pos = intrev32ifbe(is->length); return 0; /*如果待查元素小于集合头部元素,则直接返回待查元素位置为0*/ } else if (value < _intsetGet(is,0)) { if (pos) *pos = 0; return 0; } } /*二分查找*/ while(max >= min) { mid = ((unsigned int)min + (unsigned int)max) >> 1; cur = _intsetGet(is,mid); if (value > cur) { min = mid+1; } else if (value < cur) { max = mid-1; } else { break; } } /*找到元素返回1,否则返回0,pos为元素应该位置*/ if (value == cur) { if (pos) *pos = mid; return 1; } else { if (pos) *pos = min; return 0; } }
5.删除元素
intset *intsetRemove(intset *is, int64_t value, int *success) { uint8_t valenc = _intsetValueEncoding(value); uint32_t pos; if (success) *success = 0; /*查找元素是否存在*/ if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) { uint32_t len = intrev32ifbe(is->length); if (success) *success = 1; /*删除元素,并移动其他元素覆盖原来位置,这里没有缓存空间,而是直接重置原来空间,可能是为了节省内存*/ if (pos < (len-1)) intsetMoveTail(is,pos+1,pos); is = intsetResize(is,len-1); is->length = intrev32ifbe(len-1); } return is; }
总结
intset实质就是一个有序数组,可以看到添加删除元素都比较耗时,查找元素是O(logN)时间复杂度,不适合大规模的数据。我们在进行sadd,sdel等对无序集合进行操作时,并不是一定使用intset进行保存数据,后面我们讲到这几个命令时会详细讲解,操作时使用的存储策略。相关文章推荐
- Redis之Hash数据结构
- Sting 串实现
- 数据结构笔试题---栈
- 数据结构排序之快速排序
- 数据结构 — 5.二叉树层次遍历
- 中国大学MOOC-陈越、何钦铭-数据结构 排序(堆排序)
- 中国大学MOOC-陈越、何钦铭-数据结构 排序(希尔排序)
- 双向链表实现
- 数据结构问题集锦 - Basic Calculator
- 【数组项目1 - 猴子选大王(数组版)——第9周】
- 第7周SHH数据结构—【项目4 - 队列数组】
- 《数据结构(严蔚敏版)》学习笔记(一)——常用数据结构定义:
- 数据结构实验之二叉树的建立与遍历
- 第7周SHH数据结构—【项目3 - 负数把正数赶出队列 】
- 数据结构学习(二)字符倒序输出的C语言实现
- 数据结构与算法面试题80道
- P1091环城旅行 - 数据结构(模拟题)
- 单链表反转,要求空间复杂度O(1)
- 数据结构2---算法概念
- 数据结构学习(一)字符串截取的C语言实现