使用方法

map用于维护KV对,set以集合的身份维护元素。从命名角度可以知道set中元素不重复,符合数学的定义。结合计算机的底层实现,set中的元素可以按照一定的顺序组织,比如大小,字典序。同时极具计算机特色的一个用法是multiset的使用,允许一个set中有重复元素的出现。代码如下:

#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
int main(){
    unordered_multiset<string> _set;
    _set.insert("Scala");
    _set.insert("Haskell");
    _set.insert("C++");
    _set.insert("Java");
    _set.insert("Erlang");
    _set.insert("Erlang");
    unordered_multiset<string>::iterator it;
    for(it = _set.begin(); it != _set.end();it++){
        cout << *it << endl;
    }
    return 0;
}

从用法上违背了数学中set的定义。至于为什么有这种用法?一种可能的理由是为了充分使用set底层的数据结构,原理可以从底层实现部分看到,同时此处的一个坑在部分提到。map用于维护KV对,代码 如下:

#include <iostream>
#include <unordered_map>
#include <map>
#include <string>
using namespace std;
int main(){
    unordered_map<int, string> map;
    //map<int, string> _map;
    _map.insert(make_pair(1,"Scala"));
    _map.insert(make_pair(2,"Haskell"));
    _map.insert(make_pair(3,"C++"));
    //_map.insert(make_pair(4,"Java"));
    _map.insert(make_pair(4,"JavaScript"));
    _map.insert(make_pair(5,"Erlang"));
    
    for(auto elem:_map){
      cout << elem.first << "," << elem.second << endl;
    }
    unordered_map<int, string>::iterator it;
    //map<int, string>::iterator it;
    for(it = _map.begin(); it != _map.end(); it++){
      cout << it->first << "," << it->second << endl;
    }
    return 0;
}

KV对,就要维持K的唯一,保证K的索引作用。

底层实现

set和map及其unordered版本最大的不同体现在底层实现,其中set和map的实现都是基于红黑树,而unordered_map的实现是基于哈希表。从用法上看,set默认元素有序,unordered_set中元素无序。为啥?红黑树是一棵平衡二叉查找树,能够以O(lgN)的时间复杂度实现插入,删除,访问元素。而哈希表基于散列函数及相关冲突解决方案确定元素的最终位置,不能保证元素的顺序。这里的顺序的概念可以从遍历容器的输出中看到。既然set是基于哈希表实现,那么需要KV,现在有了V,K在哪里呢?对于unordered_set的实现,K=V,为啥?set中元素不重复,保证了K的唯一性呀。那这里问题来了,unordered_multiset的实现呢?相同的K的散列值一般应该保持相同,如果位置冲突,找到一个合理的位置应该能解决问题。

map的原理和set基本相同,map的对K进行顺序的维持。

原理决定了使用的选择。如果是查询密集型程序,显然使用基于哈希表的容器实现更好。但是哈希是用空间换时间的典型技术,故如果对内存要求比较高,基于哈希的实现就不好了。另一个方面,如果要求数据有序,显然选择基于红黑树的实现。

坑一:unordered_set和unordered_multiset都在头文件

#include <unordered_set>

中,至于为什么没有单独拿出来?可能二者在实现上的差别大于set和unordered_set的差别吧。

坑二:在向map中插入元素时,K可以不唯一,但是最终存储的元素中,只会保留K第一次出现的那个元素。代码如下:

    //_map.insert(make_pair(4,"Java"));
    _map.insert(make_pair(4,"JavaScript"));

_map中只会将”Java”成功插入。可能成为坑的一个地方在于当插入K相同的两个元素时,竟然没有警告或者报错。

参考:

1.C++ STL之哈希表