关注我的微信公众号:小争哥,获取更多、更新的技术、非技术分享。
作者:前Google工程师,5万人订阅《数据结构和算法之美》专栏作者。
希望通过我加速你的技术、职场进步。
到目前为止,我的公众号里的读者,都是我的专栏《数据结构和算法之美》的订阅者。所以,今天要讲的内容,跟专栏里的某个知识点有关。
我在专栏里面,有讲到,链表经常和散列表在一块使用,还讲到Redis中的有序集合,是通过散列表和跳表,两种数据结构来实现的。(详细内容请参看《数据结构和算法专栏》的第20讲)。
关于这两个问题,有小伙伴有一些疑问,搞不清楚两个不同的数据结构,是如何配合工作的?我这里就小伙伴们的疑问,抽象成一些共性的话题,来讲一下,什么是"多重索引结构"?
什么是索引?
索引这个话题,我们专栏的第50讲中有讲到。简单点讲,在海量数据中,查找某个数据的时候,索引作为一个辅助结构,可以起到快速定位到数据的作用。
索引可以说无处不在,在很多系统设计中,都有用到。比如MySQL中的B+树索引,搜索引擎中的倒排索引、Redis中用Key构建的散列表索引。
什么是多重索引?
所谓多重索引,就是指,对同一个数据集合(或者说是对象集合),我们对其构建多个不同的索引。你可能会说,一个索引不就足够定位数据了吗?为什么还要多重索引呢?
首先,我们要明确一个知识点。有了这个知识点,这个问题你就好理解。
我们这里所说的“数据”,以及数据结构和算法之美专栏中提到的“数据”,并不是简单的1、2、3...这样的数字。而是一个包含很多字段的对象(Object),举个例子,比如下面表示学生的Student对象:
Class Student {
public String ID; // 学号
public int score; // 成绩,假设成绩不包含小数点
}
复制代码
在真实的软件开发中,我们是拿对象的“key”,来构建数据结构。key之外的数据,我们叫做卫星数据,这部分数据不参与构建数据结构。怎么理解这句话呢?
还是拿刚刚学生对象这个例子来讲解。如果我们的需求是,通过学号,能快速的查找某个学生的信息(比如成绩)。那我们就把“ID”作为key,构建散列表。构建成的散列表结构如下所示:
现在,如果我们的需求变了,不但需要通过学号来快速的查找学生的信息(比如成绩),还需要通过成绩区间(比如查询大于60分小于70分的有哪些同学),快速的查找学生信息列表(比如学生的学号),那又该怎么办呢?
显然,基于学号构建的散列表,这样一个索引,是无法实现两种快速查找需求的。怎么办呢?我想,你应该已经能想到解决办法了吧,那就是,我们可以再把score作为key,再构建一个索引。
跳表这种数据结构,我们在专栏里有讲过,我们可以基于学生信息中的成绩字段,构建成跳表这种索引结构。你如果忘记了,可以去再去回顾一下那节课的内容。
现在,基于ID和Score,我们构建了两个索引(多重索引),就如下图所示,通过散列表,我们可以快速的通过学号定位学生对象,通过跳表,我们可以快速的查找,属于某个成绩区间的学生有哪些。
这里本该添加一张图的,我手画了一下,发现太乱了,没法看,抽空找个绘图软件来画。 不过,这里,我需要强调一点,也是很多小伙伴,有疑惑的地方。数据(学生对象)在内存中,永远只有一份的,索引中存储的是这些数据的内存地址(表现在代码中就是指针或者引用)。