【发布时间】:2012-03-04 22:39:21
【问题描述】:
在我当前的 C++ 项目中,我有一个 STL 映射,它将整数键映射到对象上。算法返回一组条目。返回的数据取决于算法的输入,因此无法预测:
class MyClass
{
//...
};
int myAlgorithm(vector<int>::iterator inputIt)
{
// return a key for myMap which is calculated by the current value of inputData
}
int main(int argc, char *argv[])
{
vector<int> inputData;
map<int, MyClass> myMap;
//<fill map with some data>
//<fill inputData>
vector<MyClass> result;
for (vector<int>::iterator it = inputData.begin(); it != inputData.end(); it++)
{
int myMapKey = myAlgorithm(*it);
// count() > 0 means "check whether element exists. Performance can be improved by replacing
// the operator[] and count() calls by map::find(). However, I want to simplify things
// in this example.
if (myMap.count(myMapKey) > 0)
{
// in some cases there is no entry in myMap
result.push_back(myMap[myMapKey]);
}
}
}
如示例中所述,我可以用 find 替换 map::count() 和 operator[]-calls。 STL-reference 表示 map::find() 的复杂度是对数大小 (O(log n))。
我发现在大多数情况下,myMap 中的条目对于结果中的两个后续条目非常接近。因此,我得出的结论是,如果我用迭代器替换 map.find() 调用,我将获得更好的性能:
map<int, MyClass>::iterator myMapIt = myMap.begin();
for (vector<int>::iterator it = inputData.begin(); it != inputData.end(); it++)
{
int myMapKey = myAlgorithm(*it);
// just increment iterator
while (myMapKey != myMapIt->first)
{
myMapIt++;
// we didn't find anything for the current input data
if (myMapIt == myMap::end() || myMapIt->first > myMapKey)
{
break;
}
}
// I know that I'm checking this twice, but that's not the point of my
// question ;)
if (myMapIt == myMap::end() || myMapIt->first > myMapKey)
{
// probably it would be better to move the iterator back to the position
// where we started searching, to improve performance for the next entry
myMapIt = myMap.begin();
}
else
{
result.push_back(myMapIt.second);
}
}
这个概念可行,但我有一个大问题:根据 inputData,我必须向前或向后搜索。考虑到我多次调用main() 中的代码,并且这些调用的 inputData 发生了变化。我可以在进入for-loop 之前决定是否增加或减少while-loop 中的迭代器。
我认为只需将map<>::iterator 切换为map<>::reverse_iterator 并使用rbegin()/rend() 而不是begin()/end() 就可以了。但后来我意识到reverse_iterator 和iterator 没有共同的基类:
map<int, MyClass>::base_iterator myIt;
if (/* ... */)
{
myMapIt = myMap::begin();
myMapEndIt = myMap::end();
}
else
{
myMapIt = myMap::rbegin();
myMapEndIt = myMap::rend();
}
/* for (...) ... */
那太好了,但是没有base_iterator。
我知道这个问题的简单解决方法:我只需要复制整个 for-loop 并针对这两种情况进行调整:
if (/* ... */)
{
/* for(...) which uses normal iterator in the while-loop */
}
else
{
/* for(...) which uses reverse iterator in the while-loop */
}
非常糟糕...您知道更好的解决方案吗?
【问题讨论】:
-
调用函数模板会起作用吗?
-
您是如何得出结论的,即您将获得更好的性能?你有数据备份吗?如果这不是您的应用程序中的真正瓶颈,您可能只是为自己做更多的工作。也就是说,这仍然是一个有趣的问题。 :)
-
由于使用
map::find()时的 O(log n) 复杂性,无法假设下一个条目接近当前条目。这段代码处于非常关键的位置,在几个嵌套循环中
标签: c++ stl map iterator polymorphism