当前位置: 首页 > news >正文

【C++】unordered_map在Windows和Linux上的不同行为

我目前手头上的项目,需要编译在板端Linux上运行,但是日常daily调试多在Windows上开发。这就涉及到同一份代码在多平台上的编译个运行。有一次遇到了一个奇怪的现象:跑同样的一份代码,Windows和Linux出来的结果是不一致的。最终确定到不一致的原因出现在unordered_map上,就把这次记录总结下来。

这次不一致发生在:处理一个状态序列的投票操作。从编程的角度而言,最适合投票操作的容器就是键值对,key放置投票的内容,value放置投票的次数。在C++中,STL提供了两个相关容器:map和unordered_map。在代码上选择了unordered_map来实现。


问题复现

我把这个问题简化:假设车的类型有四种情况,分别为UNKNOWN、SMALL、MIDDLE、BIG。现有15次的估计,以出现次数最多的类型作为其最终类型。代码非常简单:

#include <iostream>
#include <unordered_map>
#include <string>int main()
{std::unordered_map<std::string, int> vehs;vehs["UNKNOWN"] = 5;vehs["SMALL"] = 3;vehs["MIDDLE"] = 5;vehs["BIG"] = 2;std::string max_type;int max_times = -1;auto iter = vehs.begin();for (; iter != vehs.end(); ++iter) {if (iter->second > max_times) {max_times = iter->second;max_type = iter->first;}std::cout << iter->first << " ";}std::cout << std::endl;std::cout << "max_type : " << max_type << std::endl;return 0;
}

在Windows平台下,编译并运行代码:

BIG UNKNOWN SMALL MIDDLE
max_type : UNKNOWN

在Linux平台下,编译并运行代码:

BIG MIDDLE SMALL UNKNOWN
max_type : MIDDLE

可以看到,在Windows和Linux两个平台下的运行结果中,出现次数最多的车的类型,一个为UNKNOWN,一个为MIDDLE。主要原因是这两种类型都是5次(最多)。这时,unordered_map的遍历顺序就很重要了,这将直接关系到最终的输出结果。通过打印结果可以看到,两个平台下的遍历顺序是不一致的。

在C++中,STL提供了两个键值对容器:map和unordered_map。上面的代码,采用的是unordered_map容器,如果将其改成map容器呢?还会发生这种不一致的结果么?

std::map<std::string, int> vehs;    // 将unordered_map修改为map

此时再在Windows和Linux平台下,分别编译并运行代码,最终的输出都是一致的:

BIG MIDDLE SMALL UNKNOWN
max_type : MIDDLE

也就是说,这种平台不一致性只发生在unordered_map容器上,而不发生在map容器上


原因分析

map和unordered_map分别在Windows和Linux上的不同运行结果,主要体现在遍历顺序上。对于map而言,两个平台上的遍历顺序是一致的;而对于unordered_map而言,两个平台上的遍历顺序是不一致的。

遍历顺序的不同,究其根本,主要体现在其内部构造的不同

map的内部构造:

  • map的内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树具有自动排序的功能,因此map内部的所有元素都是有序的。红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。

unordered_map的内部构造:

  • unordered_map的内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的。

两者在运行效率和占用内存的对比:

  • 运行效率方面:unordered_map最高,而map效率较低,但提供了稳定效率和有序的序列
  • 占用内存方面:map内存占用略低,unordered_map内存占用略高,而且是线性成比例的

需要无序容器,快速查找删除,不担心略高的内存时可以选择unordered_map;需要有序容器稳定查找删除效率,内存很在意时候用map。

因此,如果需要使用map,那么key需要定义operator<,用于进行有序的排序;如果需要使用unordered_map,那么key需要定义hash_value函数并且定义operator==,用于hash值的计算。而:

  • 当使用map时,Windows和Linux对于operator<的定义都是一致的
  • 当使用unordered_map时,Windows和Linux对于operator==的定义都是一致的,对于hash_value函数的定义是不一致的

从而,在使用unordered_map时,Windows和Linux使用两种不同的哈希函数,产生不同的哈希码,从而依次产生不同的存储区放置,导致在迭代时产生不同的顺序


相关阅读

  • c++ - Windows 和 Linux 上 std::unordered_map 容器的不同行为
http://www.lryc.cn/news/108666.html

相关文章:

  • Apipost三方消息通知,接口变更不用愁
  • C语言 用数组名作函数参数
  • 每日一题(980. 不同路径 III)-回溯
  • 【Python:json常用函数,用于加载和保存json文件】load(), loads(), dump(), dumps()
  • Flink State 和 Fault Tolerance详解
  • 小红书2023“家生活”趋势白皮书
  • 使用 LangChain 搭建基于 Amazon DynamoDB 的大语言模型应用
  • 210. 课程表 II Python
  • 【LeetCode 算法】Linked List Cycle II 环形链表 II
  • 蒸散发与植被总初级生产力估算
  • uniapp微信小程序底部弹窗自定义组件
  • 人工智能的最新进展:2024年将会发生什么?
  • 使用Golang实现一套流程可配置,适用于广告、推荐系统的业务性框架——组合应用
  • DNS入门学习:DNS缓存的原理和作用(中科三方)
  • Linux虚拟机安装tomcat(图文详解)
  • Matlab对TMS320F28335编程--SVPWM配置互补PWM输出
  • MySQL数据库——多表操作
  • Java版本spring cloud + spring boot企业电子招投标系统源代码 tbms
  • css实现,正常情况下div从左到右一次排列,宽度超出时,右侧最后一个div固定住,左侧其他div滚动
  • 【Linux手动搭建Sftp,创建用户、用户组及删除用户】
  • 云上 Index:看「简墨」如何为云原生打造全新索引
  • Linux安装cuda和cudnn教程
  • 短视频矩阵源码
  • 群狼调研—连锁化妆品品牌门店神秘顾客调查的行家
  • C# 回文链表
  • 基于freertos的温湿度蓝牙系统
  • 华为云CTS 使用场景
  • 【css】nth-child选择器实现表格的斑马纹效果
  • 找视频素材就上这8个网站,免费可商用,马住了。
  • Springboot部署ELK实战