静态链表的基本结构网上到处都是,大学课本里也很详细,不熟悉的同学请看下面我大学时课件抓图,在此感谢楼老师。(说实话,大学那么多课,只有您的课仔细听过:-))
数据结构课程设计帮同学做过好几个静态链表的题,没想到毕业两年多来用这东西做过好几个优化,分别如下:
1、DES密钥缓存
金融交易安全类项目,存储的就是普通的Key-Value结构,Key是密钥名,Value是DES密钥,都为短字符串,原来的方案是开个共享内存做数组,按Key排序,折半查找还算说的过去,但插入和删除就是O(n)了,所以在业务高峰时期就悲剧了……
有些同学可能想“为什么要用共享内存?直接用一个STL map/unordered_map就直接搞定。为什么不用现有的memcached/redis……解决方案……”,记得后来和coolshell的陈皓老师email交流过,他也有这类疑问;原因大概有这样几个:
故只能在原来的方案上优化,而且设想的是要将密钥数据和所用查询机制的索引之类的东西分开存放;大概如下:
1、 开2块共享内存A、B;A用来存放索引信息,B用来存放实际的密钥数据。
2、 A中就是普通的散列表,线性再散列法,存储密钥名对应的密钥在B中的静态链表中的下标值。
3、 共享内存内不可能用传统的链表结构,所以在B中使用双向静态链表存储密钥数据。
A中散列表存的是数据在B中静态链表中的下标值为关键,实现O(1)访问;如果不把密钥数据和索引数据分开存放,就不用B共享内存,直接用A存储密钥即可。
这也算是我第一次将静态链表使用于应用中。课本中告诉我们静态链表一般用于没有指针的如BASIC/FORTRAN之类的语言,它和普通链表一样,插入删除不需要移动元素,无法随机存储等等…… 什么?无法随机访问?如果我们直接拿数据在静态链表中的下标不就可以直接访问了么?
2、通信层和应用层映射
有些同学会在通信层接受连接后生成一个类似可读的client_id之类的值作为通信层和应用层之间交互的映射,映射的方法是通过查hash map实现,但是这里的查询是非常频繁的,哪怕是hash map也会带来一定的效率损耗。
熟悉Reactor机制实现的同学知道底层通信可以使用一个max_fd大小的数组作为fd到Event_Handler结构的映射,在C++中的写法如下:
std::vector<Event_Handler *> fd_map(max_fd(), (Event_Handler *)0);
epoll_wait返回的fd集合可以直接以O(1)的效率映射到对应的Event_Handler,fd和client_id都存在Event_Handler中。
好,现在解决了fd->Event_Handler->client_id的映射,那反过来client_id->Event_Handler->fd呢?
简单的生成client_id的方法可能是一个int从0递增,通过client_id去hash map查询Event_Handler;可能为如下结构:
std::unordered_map<int, Event_Handler *> fd_client_id_map(max_fd());
但即便是hash map也比数组下标访问有一定效率损失;说到这里可能大家也知道了,如果以静态链表存放Event_Handler *,client_id就是Event_Handler *在一个静态链表中的下标,那么就可以直接通过下标取得Event_Handler *,也就以O(1)打通了client_id->Event_Handler->fd这条路。
Event_Handler *存储在一个静态链表中,它所在的下标作为其client_id,这个静态链表用STL vector实现,可做成动态增加、减少大小;不用做成固定大小,
还有其他几个和具体应用相关的静态链表的使用。
总结:
后来仔细想了下,其实凡是需要动态生成一个id,并且需要用此id做到另一个东西的映射都可以如上方式使用静态链表,实现真正的O(1)。
文章下载 http://files.cnblogs.com/logicbaby/%E9%9D%99%E6%80%81%E9%93%BE%E8%A1%A8%E5%BA%94%E7%94%A8.pdf