源码与官方文档路径
flat_map.h(486行代码)
flat_map_inl.h(657行代码)
flatmap.md
原理简析
如图所示,flatmap使用的是开链哈希。使用时先申请一块N*sizeof(Bucket)大小的连续虚拟内存。Bucket本身会存储value值,因此当哈希算法足够优秀时(即不会有拉链或很短的拉链),可以实现接近原生数组的查找性能。
数组大小的选取有两种方式:
- 数组大小为素数,当除以一个素数时,会产生最分散的余数
- 数组大小为2的指数倍:这会屏蔽被除数中的位,可能是最糟糕的选取方式。但是好处是可以通过位移运算提高计算余数的效率。
flatmap默认选取的是第二种方式,因为作者认为flatmap选取的哈希算法murmurhash3已经足够好了
主要数据结构介绍
Bucket
如上图所示,我们先看一下Bucket结构的实现,Bucket结构体是在Flatmap类内部实现的:
struct Bucket {
explicit Bucket(const _K& k) : next(NULL)
{ new (element_spaces) Element(k); }
Bucket(const Bucket& other) : next(NULL)
{ new (element_spaces) Element(other.element()); }
bool is_valid() const { return next != (const Bucket*)-1UL; }
void set_invalid() { next = (Bucket*)-1UL; }
// NOTE: Only be called when in_valid() is true.
Element& element() {
void* spaces = element_spaces; // Suppress strict-aliasing
return *reinterpret_cast<Element*>(spaces);
}
const Element& element() const {
const void* spaces = element_spaces;
return *reinterpret_cast<const Element*>(spaces);
}
Bucket* next;
char element_spaces[sizeof(Element)];
};
在指定分配好的内存位置new对象的用法参考:C++ new 的用法 (总结)
我们可以看到,每个Bucket都有一个指针指向一整个开链,还有一个存储kv对象的element。对于该位置上没有元素的Bucket,我们将其next指针指向-1表示非法,不再需要额外的标记位。
至于这里为什么需要用char数组的方式存储Element对象,不直接存放一个Element对象,个人觉得应该是都可以,用char数组的方式可能看起来更加解耦。
FlatMapElement
也就是Bucket中的Element,这也是个模板类
template <typename K, typename T>
class FlatMapElement {
public:
typedef std::pair<const K, T> value_type;
explicit FlatMapElement(const K& k) : _key(k), _value(T()) {}
const K& first_ref() const { return _key; }
T& second_ref() { return _value; }
value_type& value_ref() { return *reinterpret_cast<value_type*>(this); }
inline static const K& first_ref_from_value(const value_type& v)
{ return v.first; }
inline static const T& second_ref_from_value(const value_type& v)
{ return v.second; }
private:
const