C++ 在std::vector和std::无序_映射之间进行选择,以便在少数项目情况下进行搜索?

C++ 在std::vector和std::无序_映射之间进行选择,以便在少数项目情况下进行搜索?,c++,algorithm,performance,c++11,stl,C++,Algorithm,Performance,C++11,Stl,有几个项目需要迭代和按键搜索。我已经为迭代形成了一个std::vector。我是否需要形成一个struct以进行搜索,例如std::unordered\u map 我知道在std::vector中搜索导致O(N),在std::unordered\u map中搜索导致O(1)。但里面的物品大约有10件。初始化后未发生插入或更新。我可能会搜索很多次。也许一百万,十亿,甚至更多,我不能确定 我担心散列可能比迭代成本更高 以下是一个示例: class Item { public: int key

有几个项目需要迭代和按键搜索。我已经为迭代形成了一个
std::vector
。我是否需要形成一个
struct
以进行搜索,例如
std::unordered\u map

我知道在
std::vector
中搜索导致
O(N)
,在
std::unordered\u map
中搜索导致
O(1)
。但里面的物品大约有10件。初始化后未发生插入或更新。我可能会搜索很多次。也许一百万,十亿,甚至更多,我不能确定

我担心散列可能比迭代成本更高

以下是一个示例:

class Item
{
public:
    int key;
    const char* value;
};

class Items
{
public:
    Items(const std::vector<const Item> items) 
    : _vector(items)
    , _map(generateMap()){
    }

    const char* getValueByKey(int key) const {
        //which one to choose
        //map
//        const auto& iter = _map.find(key);
//        if (iter!=_map.end()) {
//            return iter->second;
//        }
//        return nullptr;
        //vector
        for (const auto& iter : _vector) {
            if (iter.key==key) {
                return iter.value;
            }
        }
        return nullptr;
    }

protected:
    const std::unordered_map<int, const char*> generateMap() const{
        std::unordered_map<int, const char*> map;
        for (const auto& item : _vector) {
            map.insert({item.key, item.value});//I can make sure that no same key will exists
        }
        return map;
    }

    const std::vector<const Item> _vector;
    const std::unordered_map<int, const char*> _map;//Is it necessary?
};

int main() 
{   
    const std::vector<const Item> items ={
        {1, "value_1"},
        {20, "value_2"},
        {10, "value_3"},
        {55, "value_4"},
    }; 
    Items theItems = items;
    srand(time(nullptr));
    for (int i = 0; i < 1000000; i++) {
        int key = rand();
        printf("%d %s exists\n", key, theItems.getValueByKey(key)==nullptr?"is not":"is");
    }
    return 0;
}
类项目
{
公众:
int键;
常量字符*值;
};
类别项目
{
公众:
项目(常量标准::向量项目)
:_矢量(项目)
,_映射(generateMap()){
}
常量字符*getValueByKey(int键)常量{
//选择哪一个
//地图
//const auto&iter=\u map.find(键);
//如果(iter!=\u map.end()){
//返回iter->second;
//        }
//返回空ptr;
//载体
用于(常数自动和iter:_矢量){
if(iter.key==key){
返回iter.value;
}
}
返回空ptr;
}
受保护的:
常量std::无序映射生成器映射()常量{
std::无序地图;
用于(常数自动和项目:_向量){
map.insert({item.key,item.value});//我可以确保不存在相同的键
}
返回图;
}
常数std::向量_向量;
const std::无序映射_map;//是否有必要?
};
int main()
{   
常量std::向量项={
{1,“值_1”},
{20,“值_2”},
{10,“值_3”},
{55,“值4”},
}; 
项目=项目;
srand(时间(nullptr));
对于(int i=0;i<1000000;i++){
int key=rand();
printf(“%d%s存在”\n),key,items.getValueByKey(key)==nullptr?“不是”:“是”);
}
返回0;
}
这是一个
int
关键案例,可能没有发生哈希。但是对于其他情况,一个
std::string
,一个用户定义的
struct
等等,又该怎么办呢


那么,理论上我应该如何对这种情况做出决定呢

政治上正确的答案是“基准!”


但根据其他人的经验,当只使用少量大小相对较小的项时,使用
std::vector
通常更快(尤其是在其排序时),因为它改进了项的内存位置,并且不会对其项使用额外的堆分配/释放。但是,如果键类似于
std::string
,并且使用其内容进行键比较,那么这当然可能会损害内存位置,因为字符串内容(不总是)包含在字符串对象本身中,但是在堆上。

如果您不打算更改数据,并且需要进行多次搜索,我建议您尝试使用
std:vector
并对其进行排序。然后,您可以利用容器已排序的事实,使用查找算法,如二进制搜索、STL的下界或上界


您得到了最好的:局部性和O(log(N))复杂性

对于少量项目,但查找次数为10亿次的情况下,我们需要看看哪一个更快,向量的短迭代与无序的_映射,如上所述,只要避免冲突,就可以提供O(1)性能。向量的一次迭代可能比映射的散列更快。接下来的问题是,对于一次平均查找,映射会加快多少项。为了确定答案,你应该在两者之间进行基准测试,看看什么能为你的特定情况提供最佳时间


或者,由于您提到初始化后不会发生插入或更新,因此如果键的范围很小,您可以使用查找表,它将以较小的内存开销为代价提供最快的性能(无哈希问题)

我想看一看
boost::flat_map
,它在向量实现上提供了一个map接口

不管O的复杂性有多大,事实上,由于数据的局部性和从主存预取数据,使用向量比使用映射时硬件的性能要好得多


引用Chandler Carruth的话,“地图是一种减慢代码速度的练习”

不要猜测,要对它们进行基准测试。但是,只有在您实际编写了一个程序并确定这是一段对您的总体性能有重大影响的代码之后,才能对它们进行基准测试。为什么要排除ordered
std::map
选项?在许多情况下,这是一个很好的折衷办法。@a.s.H为什么
std::map
会更好?@xaxxon我正在寻找一个理论上的答案。根据您提供的有限信息,没有理论上的答案。在这种情况下,二进制搜索是无用的。当你能看到最大值为O(10)即O(1)时,为什么还要谈论O表示法呢?@Shasha99感谢你强调了一个事实,即在问题中描述的特定情况下,一切都是O(1)。这是否意味着地点比其他任何东西都重要?如果我谈论O-符号,那是为了一般性。我希望我的问题会被很多人读到,我希望它能启发其中的一些人。即使在排序向量可以更好地完成工作的情况下,使用地图查找元素也是我过去犯过的一个常见错误。我希望读者能够推测这一事实。O(1)并不意味着快。这就是为什么我提到需要进行基准测试,这也是我在项目数量较少的情况下提到查找表的目的。对于任意数据的“查找表”不是地图吗?对于地图,我没有性能数据结构