STL中map与hash_map容器的选择收藏
这篇文章来自我今天碰到的一个问题,一个朋友问我使用map和hash_map的效率问题,虽然我也了解一些,但是我不敢直接告诉朋友,因为我怕我说错了,通过我查询一些帖子,我这里做一个总结!内容分别来自
alvin_lee ,codeproject,codeguru.baidu等等!
先看看alvin_lee 朋友做的解析,我觉得还是很正确的,从算法角度阐述了他们之间的问题!
还记得Herb Sutter那极有味道的《C++对话系列》么,在其中《产生真正的hash对象》这个故事里就讲了map的选择。顺便回顾一下,也讲一下我在实用中的理解。
选择map容器,是为了更快的从关键字查找到相关的对象。与使用list这样的线性表容器相比,一可以简化查找的算法,二可以使任意的关键字做索引,并 与目标对象配对,优化查找算法。在C++的STL中map是使用树来做查找算法,这种算法差不多相当与list线性容器的折半查找的效率一样,都是O (log2N),而list就没有map这样易定制和操作了。
相比hash_map,hash_map使用hash表来排列配 对,hash表是使用关键字来计算表位置。当这个表的大小合适,并且计算算法合适的情况下,hash表的算法复杂度为O(1)的,但是这是理想的情况下 的,如果hash表的关键字计算与表位置存在冲突,那么最坏的复杂度为O(n)。
那么有了这样的认识,我们应该怎么样选用算法 呢?前两天看Python文章的时候,不知道哪个小子说Python的map比c++的map快,如何如何的。但是他并不知道Python是默认使用的 hash_map,而且这些语言特征本质上是使用c/c++写出来的,问题在与算法和手段,而不是在于语言本身的优劣,你熟悉了各种算法,各种语言的细 节、设计思想,还能在这偏激的嚷嚷孰好孰坏(片面与偏激的看待事物只能表明愚昧与无知,任何事物都有存在的价值,包括技术)。显然C++的STL默认使用 树结构来实现map,是有考究的。
树查找,在总查找效率上比不上hash表,但是它很稳定,它的算法复杂度不会出现波动。在一次 查找中,你可以断定它最坏的情况下其复杂度不会超过O(log2N)。而hash表就不一样,是O(1),还是O(N),或者在其之间,你并不能把握。假 若你在开发一个供外部调用的接口,其内部有关键字的查找,但是这个接口调用并不频繁,你是会希望其调用速度快、但不稳定呢,还是希望其调用时间平均、且稳 定呢。反之假若你的程序需要查找一个关键字,这个操作非常频繁,你希望这些操作在总体上的时间较短,那么hash表查询在总时间上会比其他要短,平均操作 时间也会短。这里就需要权衡了。
这里总结一下,选用map还是hash_map,关键是看关键字查询操作次数,以及你所需要保证 的是查询总体时间还是单个查询的时间。如果是要很多次操作,要求其整体效率,那么使用hash_map,平均处理时间短。如果是少数次的操作,使用 hash_map可能造成不确定的O(N),那么使用平均处理时间相对较慢、单次处理时间恒定的map,考虑整体稳定性应该要高于整体效率,因为前提在操 作次数较少。如果在一次流程中,使用hash_map的少数操作产生一个最坏情况O(N),那么hash_map的优势也因此丧尽了。
下面先看一段代码,从Codeproject的 Jay Kint:
// mandatory contrived example to show a simple point
// compiled using MinGW gcc 3.2.3 with gcc -c -o file.o
// file.cpp
#include <string>
#include <ext/hash_map>
#include <iostream>
using namespace std;
// some STL implementations do not put hash_map in std
using namespace __gnu_cxx;
hash_map<const char*, int> days_in_month;
class MyClass {
static int totalDaysInYear;
public:
void add_days( int days ) { totalDaysInYear += days; }
static void printTotalDaysInYear(void)
{
cout << "Total Days in a year are "
<< totalDaysInYear << endl;
}
};
int MyClass::totalDaysInYear = 0;
int main(void)
{
days_in_month["january"] = 31;
days_in_month["february"] = 28;
days_in_month["march"] = 31;
days_in_month["april"] = 30;
days_in_month["may"] = 31;
days_in_month["june"] = 30;
days_in_month["july"] = 31;
days_in_month["august"] = 31;
days_in_month["september"] = 30;
days_in_month["october"] = 31;
days_in_month["november"] = 30;
days_in_month["december"] = 31;
// ERROR: This line doesn't compile.
accumulate( days_in_month.begin(), days_in_month.end(),
mem_fun( &MyClass::add_days ));
MyClass::printTotalDaysInYear();
return 0;
}
当然上面的代码完全可以使用STL来实现:
The Standard C++ Library defines certain function adaptors, select1st, select2nd and compose1, that can be used to call a single parameter function with either the key or the data element of a pair associative container.
select1st and select2nd do pretty much what their respective names say they do. They return either the first or second parameter from a pair.
compose1 allows the use of functional composition, such that the return value of one function can be used as the argument to another. compose1(f,g) is the same as f(g(x)).
Using these function adaptors, we can use for_each to call our function.
hash_map my_map;
for_each( my_map.begin(), my_map.end(),
compose1( mem_fun( &MyType::do_something ),
select2nd MyType>::value_type>()));
Certainly, this is much better than having to define helper functions for each pair, but it still seems a bit cumbersome, especially when compared with the clarity that a comparable for loop has.
for( hash_map::iterator i =
my_map.begin();
i != my_map.end(), ++i ) {
i->second.do_something();
}
Considering it was avoiding the for loop for clarity's sake that inspired the use of the STL algorithms in the first place, it doesn't help the case of algorithms vs. hand written loops that the for loop is more clear and concise.
with_data and with_key
with_data and with_key are function adaptors that strive for clarity while allowing the easy use of the STL algorithms with pair associative containers. They have been parameterized much the same way mem_fun has been. This is not exactly rocket science, but it is quickly easy to see that they are much cleaner than the standard function adaptor expansion using compose1 and select2nd.
Using with_data and with_key, any function can be called and will use the data_type or key_type as the function's argument respectively. This allows hash_map, map, and any other pair associative containers in the STL to be used easily with the standard algorithms. It is even possible to use it with other function adaptors, such as mem_fun.
hash_map my_vert_buffers;
void ReleaseBuffers(void)
{
// release the vertex buffers created so far.
std::for_each( my_vert_buffers.begin(),
my_vert_buffers.end(),
with_data( boost::mem_fn(
&IDirect3DVertexBuffer9::Release )));
}
Here boost::mem_fn is used instead of mem_fun since it recognizes the __stdcall methods used by COM, if the BOOST_MEM_FN_ENABLE_STDCALL macro is defined.
另外添加一些实战的例子:
连接是:
http://blog.sina.com.cn/u/4755b4ee010004hm
摘录如下:
首先,要说的是这两种数据结构的都提供了KEY-VALUE的存储和查找的功能.但是实现是不一样的,map是用的红黑树,查询时间复杂度为log (n),而hash_map是用的哈希表.查询时间复杂度理论上可以是常数,但是消耗内存大,是一种以存储换时间的方法.
就应用来说,map已经是STL标准库的东西,可是hash_map暂时还未进入标准库,但也是非常常用也非常重要的库.
这次所做的测试是对于100W及的文件列表,去重的表现,即是对文件名string,做map!
用到的头文件:
#include <ext/hash_map> //包含hash_map 的头文件
#include <map> //stl的map
using namespace std; //std 命名空间
using namespace __gnu_cxx; //而hash_map是在__gnu_cxx的命名空间里的
//测试3个环节:用map的效率,hash_map系统hash函数的效率及自写hash函数的效率.
11 struct str_hash{ //自写hash函数
12 size_t operator()(const string& str) const
13 {
14 unsigned long __h = 0;
15 for (size_t i = 0 ; i < str.size() ; i ++)
16 {
17 __h = 107*__h + str[i];
18 }
19 return size_t(__h);
20 }
21 };
23 //struct str_hash{ //自带的string hash函数
24 // size_t operator()(const string& str) const
25 // {
26 // return __stl_hash_string(str.c_str());
27 // }
28 //};
30 struct str_equal{ //string 判断相等函数
31 bool operator()(const string& s1,const string& s2) const
32 {
33 return s1==s2;
34 }
35 };
//用的时候
37 int main(void)
38 {
39 vector<string> filtered_list;
40 hash_map<string,int,str_hash,str_equal> file_map;
41 map<string,int> file2_map;
42 ifstream in("/dev/shm/list");
43 time_t now1 = time(NULL);
44 struct tm * curtime;
45 curtime = localtime ( &now1 );
46 cout<<now1<<endl;
47 char ctemp[20];
48 strftime(ctemp, 20, "%Y-%m-%d %H:%M:%S" , curtime);
49 cout<<ctemp<<endl;
50 string temp;
51 int i=0;
52 if(!in)
53 {
54 cout<<"open failed!~"<<endl;
55 }
56 while(in>>temp)
57 {
58 string sub=temp.substr(0,65);
59 if(file_map.find(sub)==file_map.end())
60 // if(file2_map.find(sub)==file2_map.end())
61 {
62 file_map[sub]=i;
63 // file2_map[sub]=i;
64 filtered_list.push_back(temp);
65 i++;
66 // cout<<sub<<endl;
67 }
68 }
69 in.close();
70 cout<<"the total unique file number is:"<<i<<endl;
71 ofstream out("./file_list");
72 if(!out)
73 {
74 cout<<"failed open"<<endl;
75 }
76 for(int j=0;j<filtered_list.size();j++)
77 {
78 out<<filtered_list[j]<<endl;
79 }
80 time_t now2=time(NULL);
81 cout<<now2<<endl;
82 curtime = localtime ( &now2 );
83 strftime(ctemp, 20, "%Y-%m-%d %H:%M:%S" , curtime);
84 cout<<now2-now1<<"/t"<<ctemp<<endl;
85 return 0;
86 }
1.map完成去重耗时34秒
2.hash_map用系统自带的函数,耗时22秒
3.hash_map用自己写的函数,耗时14秒
测试结果充分说明了hash_map比map的优势,另外,不同的hash函数对性能的提升也是不同的,上述hash函数为一同学,测试N多数据后得出的经验函数.
可以预见,当数量级越大时越能体现出hash_map的优势来!~
当然最后作者的结论是错误的,hash_map的原理理解错误!从第一个朋友的回答就可以体会到这个问题!
最后对于C++Builder用户,应该通过以下方法添加:
#include "stlport/hash_map"
才可以正确的使用hash_map
STL中map与hash_map容器的选择收藏的更多相关文章
- STL中map与hash_map的比较
1. map : C++的STL中map是使用树来做查找算法; 时间复杂度:O(log2N) 2. hash_map : 使用hash表来排列配对,hash表是使用关键字来计算表位置; 时间复杂度:O ...
- C++ STL 中 map 容器
C++ STL 中 map 容器 Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它 ...
- C++ STL中Map的按Key排序和按Value排序
map是用来存放<key, value>键值对的数据结构,可以很方便快速的根据key查到相应的value.假如存储学生和其成绩(假定不存在重名,当然可以对重名加以区 分),我们用map来进 ...
- C++ STL中Map的相关排序操作:按Key排序和按Value排序 - 编程小径 - 博客频道 - CSDN.NET
C++ STL中Map的相关排序操作:按Key排序和按Value排序 - 编程小径 - 博客频道 - CSDN.NET C++ STL中Map的相关排序操作:按Key排序和按Value排序 分类: C ...
- C++中的STL中map用法详解(转)
原文地址: https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html C++中的STL中map用法详解 Map是STL的一个关联容器,它提供 ...
- C++ STL中Map的按Key排序跟按Value排序
C++ STL中Map的按Key排序和按Value排序 map是用来存放<key, value>键值对的数据结构,可以很方便快速的根据key查到相应的value.假如存储学生和其成绩(假定 ...
- STL中map的使用
知识点 C++中map提供的是一种键值对容器,里面的数据都是成对出现的.map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的. ...
- stl中map的四种插入方法总结
stl中map的四种插入方法总结方法一:pair例:map<int, string> mp;mp.insert(pair<int,string>(1,"aaaaa&q ...
- STL中map用法
Map是 STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于 这个特性,它完成有可能在我们处理一对一数据的 ...
随机推荐
- 【转】图文并茂 Ubuntu使用Thunderbird方法指南
原文网址:http://os.51cto.com/art/201101/243445.htm Ubuntu 是一个启动速度超快.界面友好.安全性好的操作系统,它适用于桌面电脑.笔记本电脑.服务器以及上 ...
- Array vs Linked List
Access: Random / Sequential 1. Array element can be randomly accessed using index 2. Random access f ...
- Colorful Lecture Note(手工栈)
题目1 : Colorful Lecture Note 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Little Hi is writing an algorithm ...
- How to run OFBiz as a Service on linux
Windows See this specific guide: How to Run OFBiz as Windows Service with Java Service Wrapper Linux ...
- 关于打开Eclipse时出现eclipse failed to create the java virtual machine与locking is not possible in the directory问题的解决
今天在机子上使用Eclipse时候打开发现这两个问题,通过查阅资料膜拜大神博客得知解决方法,特此整理下来,方便后来遇到此问题的小伙伴们. 一开始打开Eclipse时候出现问题现象1,问题1解决以后就出 ...
- Javascript刷新页面的几种方法:
Javascript刷新页面的几种方法: 1 history.go(0) 2 window.location.reload() window.location.reload(true) ...
- 国内ip信息库的组建
1.从 APNIC 分析得到国内的段 数据源位置:http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest 2.从QQ纯真库分析得到国 ...
- 给SharePoint页面加入自己定义页脚Custom footer
给SharePoint页面加入自己定义页脚Custom footer 在公司做站点设计项目时,须要在页面上加入页脚. 非常多人都把页脚忽视了,认为没什么多大用处,事实上 ...
- [Hapi.js] POST and PUT request payloads
hapi makes handling POST and PUT payloads easy by buffering and parsing them automatically without r ...
- POJ1742:Coins(多重背包)
Description People in Silverland use coins.They have coins of value A1,A2,A3...An Silverland dollar. ...