作为软件开发者,必然会用到各种各样的容器:vector(array),list,map,set,tree等等(主要针对C++)。那么,其中的区别是什么么?官方教程(数据结构教科书)有些简单的对比、对其特征有描述。但是,真正用的时候,却仍然觉得飘忽,或者说凭感觉用,没有道理可言。
今天,突然来了点灵感,这里讲讲我的感受。
容器,顾名思义,就是用来放东西的。那么,这些东西都是有一定的特征的。根据这些不同的特征,会选择不同的容器。
1、顺序性(注意,这里的顺序,指的是放进容器的先后顺序,而非数据大小顺序)
这种特征的数据,很明显用顺序性的数据结构,vector(array),list等(stack是先进先出,vector,list都能达到效果。所以,vector/list完全够用, 这里就不单独列出stack)。
那么,这种顺序性容器,如何区分使用呢?list对比vector最大的优势就是插入、删除特别方便。所以,一般都用list。当然,如果数据个数相对比较固定,而且主要是在尾部操作,或者全量操作,那么,用vector也是不错的。
举例:socket通信,获取N个消息指针,丢队列放其他线程处理。
1)list的情况:如果其他多个线程线程,一次就从队列取一个消息,那么(头删除,尾插入,插入删除特别频繁)。
2)vector情况:如果,其他线程就一个,将消息全部取出缓存(vector可以swap),再一个个处理。处理结束后再全部清除(vector可以用clear)。
附注:当然,情况2用list也能达到相同的效果。但是,这种特征下,vector比list快,而且遍历方便。
2、查询型数据结构,保存key/value型数据对象
许多数据需要提供查询,一般都有一个key。这种数据,就需要用到查找型数据结构了。
查找型数据结构,一般主要有tree、hash表。
1)hash表没啥好说的,就是通过散列函数进行映射,所以速度相对很快(比较好的情况,复杂度为O(1))。C++中有unordered_map/unordered_set等。
2)tree有查找树,AVL-Tree,RB-Tree,B-Tree,B+Tree。RB-Tree就是C++中的map实现。B树(B/B+)主要用于索引建立(数据库,磁盘读写用的比较多)。(C++中,b树没有具体的实现容器,需要自己实现)。
查找树可以提高查询速度,但是,可能会不平衡;所以就产生了AVL-Tree。AVL-Tree操作很复杂,于是有了相对要求不是很严格的RB-Tree(map)。
B/B+是多叉树(平衡的)。这样就降低了高低,降低了查询次数。所以,一般用来做索引。
举例:一般用到的就是map和hash表。如果仅仅是查询,对key没有顺序要求,那么就用hash表好了。如果对key的顺序性要求,那么用map。
3)skiplist。是list的扩展。我自己也不知道使用场景,没做过对比。使用场景,就见过redis中有。
网上介绍跳表,说到的存在价值,一般都会说,rb-tree太复杂。skiplist实现简单。所以,我想它的存在意义就是自己实现简单,所以,更多是存在于C语言中(C++中有map,skiplist也许并不存在特别优势)
特别说明:heap是一种排序算法,不是数据结构(一般用数组或者树来实现)。一般用于top10等应用。
3、无特征数据对象。
这种数据对象没有任何要求,各个对象没有任何差别,完全相同的意义:没有顺序,没有key。这种数据一般用list来存放(一般要经常性取出/放入)。不用查找型数据结构是因为没有key。
举例:一般的资源型对象就没有差别。比如说,内存池的一个个内存块对象(相同大小是内存块是没有区别的)。所以,一般用list来存放。