1、高效地访问
1)隐式插入问题
map初学者会直接用中括号访问,方便但是有代价:key不存在的时候还会隐式插入,
尤其对于LRU缓存设计,用这个就是灾难,会导致脏数据,
规避方案,cache的visit函数采用const修饰,同时采用下面的方案访问,
2)多遍历一次的性能问题
cpp
Value LRUCache::Get(const KeyType& key, ValueType& value) {
if (map_.count(key) > 0) {
return map_[key];
return ValueType();
}
Value LRUCache::Get(const KeyType& key, ValueType& value) {
if (map_.find(key) != map_.end()) {
return map_[key];
return ValueType();
}
使用迭代器更高效,当map非常大的时候,差异更显著
cpp
Value LRUCache::Get(const KeyType& key, ValueType& value) const {
auto iter = map_.find(key);
return iter == map_.end() ? ValueType() : iter->second;
}
2、insert失败的情况
insert返回是一个pair,支持两种类型。在实际工程项目中,很少见c++代码中处理insert失败的情况,
最近居然踩了一个坑,车道级渲染合批以后,没有道路了,
- 深入分析结果是查找失败,但是insert也失败了。
- 合批merge的时候,一种特别的case,导致了丢包的操作,mesh丢了,结果非常恶劣。。
key compare的问题,整个map不再是排序树了,示例代码如下,
comparator 的两种写法
cpp
// 写法1
bool MergedMateriallKey::operator<(const MergedMateriallKey &o) const
{
return color1 < o.color1 || color2 < o.color2 || tex < o.tex || mix_tex < o.mix_tex;
}
// 写法2
bool MergedMateriallKey::operator<(const MergedMateriallKey &o) const
{
if (color1 != o.color1)
return color1 < o.color1;
if (color2 != o.color2)
return color2 < o.color2;
if (tex != o.tex)
return tex < o.tex;
return mix_tex < o.mix_tex;
}
主要差别点:
- 写法1,分支太多,不是严格的排序树,
- 写法2,只要color1不等就直接用他排序,用color2比较的唯一前提是color1相等了,
代码鲁棒的建议
key越少越好,2个int转化成一个long long,
字符串用hash值,或者转化成指针比较,
实在没招就diy结构体,key越少越好。
题外话
stl map的insert失败是静默的,返回值很容易被无效,
不像C#那么猛烈,如果key重复了直接抛异常exception,如果你不处理就直接crash,