横向思考:C++ map,Java hashmap,Python dict之间的区别

思考:

其实我是从Java->Python->C++这个顺序学的,所以三门语言都了解一点皮毛。随着学习的深入,越来越发现语言的共通,很多内容本质上都是差不多的。比如这个map,虽然三门语言里叫法不一样,但其实用法都差不多。(此前特地回去看了很多资料,java和python实在有些忘了)

C++ map与unordered_map

首先不得不说C++有两种map:map与unordered_map,它们都属于STL标准库里的容器,需要使用时需要在头文件里面引用。
对于map而言,它在C++里是这么用的:

#include <map>
#include <iostream>
int main(){
    std::map<int,int> m1={{1,2}};  //初始化赋值,每一个键对都要用{}单独再括起来,哪怕只有一对
    std::map<int,int> m2={{1,2},{3,4}};
    std::cout<<m2[1]<<std::endl;   //输出结果为2,可见直接map[key]的形式可以访问value
    std::cout<<m2[5]<<std::endl;   //输出结果为0,可见若是不存在的key,默认值为0  
    system("pause");
    return 0;
}

而对于unordered_map而言,用法和map一样,只需要把上述代码中的map全部替换成unordered_map即可。运行之后你会发现,map和unordered_map的输出结果竟然一摸一样,如果结果都一样的话,那map和unordered_map到底有什么区别?

下面我们要进一步思考map和unordered_map的内部属性了:

1.Map的原理

如果你有了解过map的实现原理的话,你会发现map的实现其实是底层采用了我们数据结构的一种特殊结构,叫做红黑树(其实我在学C++之前也不知道这东西,感觉考研数据结构都白考了)
如果没听过红黑树没关系,AVL平衡二叉树你一定听过。平衡二叉树是一种特殊的二叉排序树,而这里红黑树又是一种特殊的平衡二叉树,很奇妙吧。
相对于平衡二叉树的“强”平衡(平衡因子绝对值<=1),红黑树的要求就没那么严格,它是一种弱平衡的二叉树,顾名思义节点用红色或黑色来表示。网上大佬的详解有很多,我只简单说说几个要点
__
1.根节点一定是黑色的
2.节点非黑即红
3.任何一个红色节点的两个子节点都是黑色的
4.任何一条路径上的黑色节点的个数都是一样的
5.叶子节点都是黑的
__

红黑树因为弱平衡,发生插入删除操作时,树的自身相对调整次数会比二叉平衡树要少,不像二叉平衡树这种非要平衡因子≤1,可能插入一个节点就要调整好几次,这样操作一多起来,花销就太大了。

这样的话我们便可以进一步联想到一个事实。我过去以为,向map中添加元素的元素就是无序的,因为我们按顺序添加进去,却不能按添加的顺序进行索引或者迭代取出来。然而事实上,map内部存储是有序的,因为红黑树归根到底也是一种二叉排序树,存储进去的数据,是按key值插入到红黑树里的,只不过我们作为用户无法分辨罢了,只要我们使用迭代器来辅助,就能有序的输出经红黑树自动排序后的键对。当然,这一般就不是我们输入的顺序了,是key值排列的顺序。要是接受不了,就去使用vector吧!

现在我们再来谈谈unordered_map

2.unordered_map的原理

相比之map,unordered_map这种数据结构才是真正的无序,因为它没有采用红黑树,而是采用的是哈希表的结构。
我想这个不用过多的解释,就是利用一定的哈希函数,将键值计算然后映射到哈希空间里,发生碰撞就采取一定的手段再映射就好了。通过计算的手段来插入,而不是比较的手段,自然就是无序的了。当Hashmap发生冲突的时候,采用的是链地址法解决冲突,即把映射地址一样的键串成一长串。
你在unordered_map中添加的任何键对,在内存中存放的位置都是不确定的,这就是和map的红黑树最大差别。一个有序,一个无序。
不过这也只是存储差别罢了,所以unorderen_map和map实际用法几乎没有差别,看用户的选择。但是也不是无条件的瞎选,这要看具体的项目内容。

效率区别
如果是查找比较频繁,建议用unordered_map,因为哈希查找只要计算一下就能找到value,时间复杂度最好的情况为O(1),特别快。但是相对的,哈希表空间肯定有冗余,装填因子一般不会达到1,因此会有空间上的开销与浪费,空间复杂度比较大。即我们常说的空间换时间
其实map的空间占用也不低,毕竟要存储很多个节点,但还是略胜unordered_map一筹的,再说了,map内部其实就是二叉树,因此各种插入删除操作的时间复杂度就是二叉树的时间复杂度,效率比较稳定,最好最坏平均的时间复杂度都是O(logN),关键还是有序!!!

以上就是C++的map和unordered_map的内容。

扫描二维码关注公众号,回复: 8838912 查看本文章

Java Hashmap

讲道理很久没写java了,一开始只记得用过一个Hashmap,也是键对形式存储,现在想想英文名字都叫哈希,该不会其实就是c++的unordered_map吧。于是去查了查各种资料,现在只能自己来分析一下。
先看看别人的原话:

TreeMap和HashMap的区别 HashMap通过hashcode对其内容进行快速查找,而
TreeMap基于红黑树的一种访问的Map,所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。存取的时间复杂度都是O(log(n))
HashMap:适用于在Map中插入、删除和定位元素。Treemap:适用于按自然顺序或自定义顺序遍历键(key)。
原文链接

可以看到,原来不止有Hashmap,原来还是有Treemap呢。TreeMap是基于红黑树,Hashmap是基于哈希表的
所以我猜测C++的Map=Java的TreeMap,而C++的unordered_map=Java的Hashmap,应该只有提供的函数方法有些许差别而已。

当Hashmap发生冲突的时候,也是采用的是链地址法解决冲突,这点和C++是一样的。

直接看看java的hashmap用法,使用hashmap之前要引用hashmap的包

import java.util.HashMap;
public class test {
	public static void main(String[] args) {
		HashMap<String,Integer> m1 =new HashMap<>();
		m1.put("a", 1);
	}
}

其实在创建hashmap的时候和c++是一样的,不过在java中初始化不像c++一样可以直接带初始化列表,添加元素需要后面用put方法来添加。
我看到有人用这种方法来假装实现“初始化列表”,但感觉还是多此一举,还不如正常的添加。

HashMap<String, String> map = new HashMap<String, String>() {
    {
        map.put("name", "test");  
        map.put("age", "20"); 
    }
};

我翻大三java课的PPT课件时,发现有个例题蛮有意思的。将两个字符数组用put方法很方便地合并到了一个新的hashmap里面,下面直接把老师课件代码拷贝过来了:

import java.util.*;
public class testMap{
  static String arr1[]={"name","sex","age","dept","dorm"};
  static String arr2[]={"linda","female","19","English","3612"};
public  static HashMap  test(HashMap m,String  []a1,String []a2){
	int i=0;
	for(i=0;i<a1.length;i++) m.put(a1[i],arr2[i]);
	return m;
 }	

Python dict

python把它的映射关系数据结构叫做dict,其实我觉得就是c++的map。
查阅相关资料之后,发现事实上,dict就是采用的哈希表的结构,和c++的unordered_map和java的hashmap非常相似,但是在解决冲突的方法上,就截然不同了。dict在发生碰撞时采用了开放寻址法,也就是会通过某些手段寻找下一个可用的位置用来存储。我们一般采用线性探测。但是听说python的算法会复杂一些,我也没往下查,读者可以自行了解。
Python里面的dict非常强大了,也非常偷懒,连引用头文件或者包都不需要,上来就自带dict。
甚至我直接这样定义就行,不需要什么额外的修饰。

dic = {'name':'Eason','age':21}
print(dic)

还记得刚才我在java的代码上举了个例子把,合并两个数组成为一个hashmap。事实上对于python来说,根本不需要那么复杂,这个合并成map的操作都封装成一个函数了,这是c++和java都没有的操作。在python将两个列表合并成字典只需要使用zip这个方法,然后就可以返回一个字典了,非常方便。

list1 = ['a','b','c']
list2 = ['1','2','3']
dic=dict(zip(list1,list2))
print(dic)

所以这样就简要分析了一下C++ map unordered_map,Java hashmap,Python dict。其实这些也不算深入,也只是我突发奇想才会写这篇文章,这只是我目前的见解,可能地方说的不对,以后深入学习发现了错误会改的。

发布了6 篇原创文章 · 获赞 2 · 访问量 355

猜你喜欢

转载自blog.csdn.net/weixin_44157488/article/details/104078798