哈希封装unordered_map和unordered_set的模拟实现
文章目录
- (一)认识unordered_map和unordered_set
- (二)模拟实现unordered_map和unordered_set
- 2.1 实现出复用哈希表的框架
- 2.2 迭代器iterator的实现思路分析
- 2.3 unordered_map支持[]
- (三)结束语
(一)认识unordered_map和unordered_set
unordered_map和unordered_set这两个容器是C++11之后才更新的。unordered_map的底层复用了哈希表来实现key/value的结构,而unordered_set的底层也是复用了哈希表,但实现的是key的结构。这两个容器的使用其实跟map和set的使用是一致的,只是map和set的底层复用的是红黑树,unordered_set和unordered_map这两个容器的使用如下代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>using namespace std;int main()
{//unordered_mapunordered_map<int, int> myunmap = { {4,4},{2,2},{8,8},{11,11},{2,2} };for (auto& e : myunmap){cout << e.first << ":" << e.second << endl;}//unordered_setunordered_set<int> myunset = { 3,4,2,1,7,0,5,6,9,8,3,3 };for (auto& e : myunset){cout << e << " ";}cout << endl << endl;//mapmap<int, int> mymap = { {4,4},{2,2},{8,8},{11,11},{2,2} };for (auto& e : mymap){cout << e.first << ":" << e.second << endl;}//setset<int> myset = { 3,4,2,1,7,0,5,6,9,8,3,3 };for (auto& e : myset){cout << e << " ";}cout << endl;return 0;
}
打印结果:
从代码和打印结果来看,unordered_map和unordered_set这两个容器跟map、set一样都支持initializer_list初始化,但是从打印结果来看,unordered_map和unordered_set支持去重+不排序,而map和set支持去重+排序,这就是它们底层复用的封装不同所导致的,map和set的底层是红黑树,迭代器在遍历时走中序遍历,所以打印出来的数据是有序的,而unordered_map和unordered_set底层是哈希表,哈希表是通过哈希桶来将值一个个存储进去的,迭代器遍历时是遍历一个个哈希桶,所以底层复用的不同,实现出来的效果也略有不同,但是这几个容器的使用方式完全是类似的。
(二)模拟实现unordered_map和unordered_set
2.1 实现出复用哈希表的框架
- 首先我们得知道,哈希表既能被unordered_map复用又能被unordered_set复用,而unordered_map的数据结构是pair<key,value>,而unordered_set的数据结构就是一个key,由于这两个容器的存储的数据结构不同,所以哈希表在实现时应该使用泛型参数T来接收unordered_map和unordered_set传过来的数据结构的类型,才能实例化出不同的哈希表,提供给这两个容器使用
- 哈希表在实现时还需要使用一个K参数来接收unordered_map和unordered_set的关键字,因为这两个容器的find/erase等一些接口都是通过关键字来实现的。unordered_set传给泛型T的就是关键字,但还是需要再传一遍,就是为了要与unordered_map保持兼容,unordered_map传给泛型T的是一堆pair值,实现find/erase等接口时就不方便,所以为了保持兼容,哈希表在实现时还需要使用一个K参数来接收unordered_map和unordered_set的关键字
- 因为哈希表实现了泛型T,不知道传过来的数据是k,还是pair<k,v>,那么insert内部进行插入时要用k对象转换成整型取模和比较相等,因为pair的value不参与计算取模,且默认支持的是key和value一起比较相等,所以在任何时候只需要比较k对象,那么我们就需要在unordered_map和unordered_set层分别实现一个MapOfKey和SetOfKey的仿函数来传给哈希表中的KeyOfT,然后在哈希表中通过KeyOfT仿函数取出T类型对象中的k对象,再转换成整型取模和k比较相等
代码实现如下:
步骤1:实现哈希表
#pragma once
#include <iostream>
#include <vector>
using namespace std;//将关键字转成可以取模,如string本身取不了模,利用ASCII码值即可,用仿函数实现
template<class K>
class HashFunc
{
public://全部转成无符号的整型,这样才能取模const size_t operator()(const K& key){return (size_t)key;}
};
//使用库里面的unordered_map和unordered_set时,我们不用给string的取模进行仿函数的实现,因为库里面为string的仿函数进行了特化
template<>
class HashFunc<string>
{
public:size_t operator()(const string& str){size_t ret = 0;for (auto& s : str){ret += s;ret *= 131;}return ret;}
};namespace li //模拟实现时防止与库里面的冲突,用了命名空间
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){ }};template<class K,class T,class KeyOfT,class Hash>class HashTable{typedef HashNode<T> Node;public:inline unsigned long _stl_next_prime(unsigned long n){static const int _stl_num_primes = 28;static const unsigned long _stl_primes_list[_stl_num_primes] ={53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = _stl_primes_list;const unsigned long* last = _stl_primes_list + _stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);//lower_bound()在迭代器的范围内取>=n的最小值return pos == last ? *(pos - 1) : *(pos);}HashTable(){//提前开好第一个质数大小的空间_tables.resize(_stl_next_prime(1),nullptr);}~HashTable(){for (int i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}bool insert(const T& data){Hash hs;KeyOfT kot;if(find(kot(data)))return false;//负载因子为1就扩容if (_n == _tables.size()){unsigned long newsize = _stl_next_prime(_tables.size() + 1);vector<Node*> newtables(newsize, nullptr);for (int i = 0; i < _tables.size(); i++){//遍历旧表,将数据挪到新表Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}//算出key在哈希表中映射的存储空间size_t hashi = hs(kot(data)) % _tables.size();//头插Node* cur = new Node(data);cur->_next = _tables[hashi];_tables[hashi] = cur;++_n;return true;}bool find(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (hs(kot(cur->_data)) == hs(key)){return true;}cur = cur->_next;}return false;}bool erase(const K& key){Hash hs;KeyOfT kot;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (hs(kot(cur->_data)) == hs(key)){if (prev == nullptr){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n = 0;};
}
步骤2:封装unordered_map和unordered_set的框架,解决获取关键字(KeyOfT)的问题
//unordered_map.h
#pragma once
#include "HashTable.h"
namespace Unordered_map
{template<class K, class V,class Hash = HashFunc<K>>class unordered_map{struct MapOfKey{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:bool insert(const pair<K, V>& kv){return _hash.insert(kv);}bool find(const K& key){return _hash.find(key);}bool erase(const K& key){return _hash.erase(key);}private:li::HashTable<K, pair<const K, V>,MapOfKey,Hash> _hash;};
}
//unordered_set.h
#pragma once
#include "HashTable.h"
namespace Unordered_set
{template<class K,class Hash = HashFunc<K>>class unordered_set{struct SetOfKey{const K& operator()(const K& key){return key;}};public:bool insert(const K& key){return _hash.insert(key);}bool find(const K& key){return _hash.find(key);}bool erase(const K& key){return _hash.erase(key);}private:li::HashTable<K,const K,SetOfKey,Hash> _hash;};
}
2.2 迭代器iterator的实现思路分析
- iterator的实现就是用一个类型去封装结点的哈希结点的指针,再通过重载运算符实现迭代器像指针一样访问的行为,这里哈希表的迭代器是一个单项迭代器
- 重载运算符中,operator++如何实现呢,iterator中有一个结点的指针,该指针指向哈希桶里的一个结点,若桶下面还有结点,则将迭代器指向下一个结点即可。若桶的下面没有结点,则需要找到哈希桶,为了能找到下一个桶,我们需要在迭代器中增加一个哈希表指针,让该指针指向哈希表,这样的话若当前桶走完了,要找到下一个桶就方便了,直接用key值计算出当前桶的位置,依次往后找不为空的桶即可,若往后找到的桶都为空,遍历到哈希尾,就可返回一个nullptr
代码如下:
步骤1:实现哈希表的迭代器,重载运算符
template<class K, class T, class KeyOfT, class Hash>
class HashTable;template<class K, class T, class KeyOfT, class Hash>
struct HashTableIterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> ht;typedef HashTableIterator<K, T, KeyOfT, Hash> Self;Node* _node; //结点的指针const ht* _ht; //哈希表指针HashTableIterator(Node* node,const ht* ht):_node(node),_ht(ht){ }T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}Self operator++(){if (_node->_next){//说明桶的下面还有结点_node = _node->_next;}else{KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();hashi++;//找下一个不为空的桶while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}else{hashi++;}}if (hashi == _ht->_tables.size()){//没有不为空的桶_node = nullptr;}}return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}
};
步骤2:begin()返回第一个桶中第一个结点指针构造的迭代器,end()返回的迭代器可以用空来表示
template<class K,class T,class KeyOfT,class Hash>
class HashTable
{typedef HashNode<T> Node;template<class K, class T, class KeyOfT, class Hash>friend struct HashTableIterator; //友元声明,方便迭代器访问哈希表中的私有成员public:typedef HashTableIterator<K, T, KeyOfT, Hash> Iterator;Iterator Begin(){for (int i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur){return Iterator(cur, this);}}return End();}Iterator End(){return Iterator(nullptr, this);}
};
上面两个步骤实现的迭代器是可以修改的,我们知道unordered_map和unordered_set的关键字是不可以被修改的,所以我们需要把unordered_set的第二个模板参数改成const K,unordered_map的第二个模板参数改成pair<const K,V>,代码如下:
步骤3:封装哈希迭代器实现unordered_map和unordered_set的迭代器
//unordered_map.h
template<class K, class V,class Hash = HashFunc<K>>
class unordered_map
{
public:typedef typename li::HashTable<K, pair<const K, V>, MapOfKey, Hash>::Iterator iterator;iterator begin(){return _hash.Begin();}iterator end(){return _hash.End();}
private:li::HashTable<K, pair<const K, V>,MapOfKey,Hash> _hash;
};
//unordered_set.h
template<class K,class Hash = HashFunc<K>>
class unordered_set
{
public:typedef typename li::HashTable<K,const K, SetOfKey, Hash>::Iterator iterator;iterator begin(){return _hash.Begin();}iterator end(){return _hash.End();}
private:li::HashTable<K,const K,SetOfKey,Hash> _hash;
};
li::HashTable<K, pair<const K, V>,MapOfKey,Hash> _hash; //unordered_map
li::HashTable<K,const K,SetOfKey,Hash> _hash; // unordered_set
步骤4:实现const迭代器
增加迭代器的模板参数
template<class K, class T, class KeyOfT, class Hash>
class HashTable; //对哈希表声明template<class K, class T,class Ref,class Ptr, class KeyOfT, class Hash>
struct HashTableIterator
{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> ht;typedef HashTableIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;Node* _node; //结点的指针const ht* _ht; //哈希表指针HashTableIterator(Node* node,const ht* ht):_node(node),_ht(ht){ }Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}
};
步骤5:实现const Begin()和const End()
template<class K,class T,class KeyOfT,class Hash>
class HashTable
{typedef HashNode<T> Node;template<class K, class T, class KeyOfT, class Hash>friend struct HashTableIterator; //友元声明,方便迭代器访问哈希表中的私有成员public:typedef HashTableIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;typedef HashTableIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;ConstIterator Begin() const{for (int i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur){return ConstIterator(cur, this);}}return End();}ConstIterator End() const{return ConstIterator(nullptr, this);}
};
步骤6:封装哈希const_iterator实现unordered_map和unordered_set的const_iterator
//unordered_map.h
template<class K, class V,class Hash = HashFunc<K>>
class unordered_map
{
public:typedef typename li::HashTable<K, pair<const K, V>, MapOfKey, Hash>::ConstIterator const_iterator;const_iterator begin() const{return _hash.Begin();}const_iterator end() const{return _hash.End();}
private:li::HashTable<K, pair<const K, V>,MapOfKey,Hash> _hash;
};
//unordered_set.h
template<class K,class Hash = HashFunc<K>>
class unordered_set
{
public:typedef typename li::HashTable<K,const K, SetOfKey, Hash>::ConstIterator const_iterator;const_iterator begin() const{return _hash.Begin();}const_iterator end() const{return _hash.End();}
private:li::HashTable<K,const K,SetOfKey,Hash> _hash;
};
2.3 unordered_map支持[]
unordered_map要支持[]主要需要修改insert返回值支持,修改HashTable中insert的返回值为pair<Iterator,bool> insert(const T& data),有了insert支持unordered_map的[]就很好实现了
代码如下:
pair<Iterator,bool> insert(const T& data)
{Hash hs;KeyOfT kot;Iterator it = find(kot(data)); //find函数的返回值也要进行修改,修改成Iteratorif (it != End())return { it,false };//负载因子为1就扩容if (_n == _tables.size()){unsigned long newsize = _stl_next_prime(_tables.size() + 1);vector<Node*> newtables(newsize, nullptr);for (int i = 0; i < _tables.size(); i++){//遍历旧表,将数据挪到新表Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}
//unordered_map.h
V& operator[](const K& key)
{pair<iterator, bool> ret = _hash.insert(make_pair(key, V()));return ret.first->second;
}
既然哈希表中的inser的返回值修改了,那么对应的unordered_map和unordered_set的insert函数的返回值也要进行修改
(三)结束语
用哈希表来模拟实现这两个容器,就得先学习底层的哈希表,不了解哈希表的友友可以看我的上一篇文章https://blog.csdn.net/muzi_liii/article/details/147519118?spm=1001.2014.3001.5501。该模拟实现简易的unordered_map和unordered_set就完成了。一起学习吧!
相关文章:
哈希封装unordered_map和unordered_set的模拟实现
文章目录 (一)认识unordered_map和unordered_set(二)模拟实现unordered_map和unordered_set2.1 实现出复用哈希表的框架2.2 迭代器iterator的实现思路分析2.3 unordered_map支持[] (三)结束语 (…...
智诚科技苏州SOLIDWORKS授权代理商的卓越之选
在当今数字化转型浪潮中,SOLIDWORKS软件以其强大的功能和广泛的行业应用,成为企业迈向智能制造的有力工具。它不仅提供直观的3D建模环境,帮助企业设计师快速创建精准的3D模型,还涵盖了从概念设计到详细设计、从样品制作到最终产品…...
【网络原理】从零开始深入理解TCP的各项特性和机制.(二)
本篇博客给大家带来的是TCP/IP原理的知识点,重点以TCP为主,接续上篇. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅🚀 …...
51单片机所有寄存器介绍
51单片机所有寄存器介绍 作者将狼才鲸创建日期2025-04-27 参考资料:Intel官方《MCS-51 Programmer’s Guide and Instruction Set.pdf》CSDN阅读地址:51单片机所有寄存器介绍 一、前言 51单片机的寄存器和ARM不一样,有自己专有的名称&…...
4.27算法题
力扣649.Dota2 参议院 649. Dota2 参议院 Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇) Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程…...
衡石科技:HENGSHI SENSE 数据权限解决方案
编写目的 本方案主要讲述 HENGSHI SENSE 的数据权限方案,即在 HENGSHI SENSE 系统中,通过同步企业内部的人员属性和组织架构等信息,实现企业内部的每一个用户对于业务数据的读取权限。 本方案的的预期读者为:HENGSHI SENSE 的…...
矩阵系统源码搭建热门音乐功能板块开发,支持OEM
在数字音乐蓬勃发展的当下,矩阵系统中的热门音乐功能板块成为吸引用户的重要部分。它不仅能为用户推荐当下流行的音乐,还能提升用户在系统中的活跃度和留存率。本文将通过详细的源码搭建过程,带你了解如何在矩阵系统中实现一个功能完备的热门…...
深入理解Android Activity生命周期
引言 在Android开发中,理解Activity的生命周期对于创建高效、稳定的应用程序至关重要。无论你是初学者还是资深开发者,掌握Activity生命周期的概念都能帮助你更好地管理资源、优化性能以及处理各种用户交互场景。本文将详细介绍Activity生命周期中的各个事件,并通过示例代码…...
【WEB3】web3.0是什么
互联网在不断发展。 我们即将翻开新的篇章,迎来翻天覆地的变化。 — Web 1.0 只能阅读信息。 它主要是供我们访问和阅读信息,只有极少数人可以真正发布内容。 — Web 2.0,即互联网目前所处的阶段,我们能够在网络上发布内容、建立…...
2025上海车展 | 移远通信重磅发布AR脚踢毫米波雷达,重新定义“无接触交互”尾门
4月25日,在2025上海国际汽车工业展览会期间,全球领先的物联网和车联网整体解决方案供应商移远通信宣布,其全新AR脚踢毫米波雷达RD7702AC正式发布。 该产品专为汽车尾门“无接触交互”设计,基于先进的毫米波技术,融合AR…...
ubuntu安装git及使用(本地git)
ubuntu安装git及使用教程(本地git) 1.ubuntu安装git1.1 查看自己的Ubuntu是否已经装有git1.2 下面进行介绍如何Ubuntu终端安装git (若已安装则可忽略) 2. 配置Git基本信息2.1 若不清楚是否配置的可使用如下命令查看2.2 未配置用户…...
数智读书笔记系列031《HIS内核设计之道——医院信息系统规划设计系统思维》书籍简介与读书笔记
一、作者与出版信息 作者团队(核心贡献者) 任连仲 身份:中国工程院院士(2022年当选),解放军总医院信息科原主任技术贡献: 主导“军字一号”系统架构设计(1997-2005年),支撑全国300余家三甲医院信息化建设提出“医疗数据语义网格”理论,获国家科技进步二等奖(2018年…...
WinForm真入门(18)——DateTimePicker控件解析
一、基本概念 DateTimePicker 是 Windows 窗体中用于选择日期和时间的控件,支持以下交互方式: 通过下拉日历选择日期通过上下按钮调整时间直接输入日期或时间 适用于需要规范日期格式、限制日期范围或快速输入的场景(如预约系统、数据…...
关于堆栈指针的那些事 | bootloader 如何跳转app
问题描述 堆栈指针的值通常存储在 App 的向量表(Vector Table)的第一个位置(0x08002000),为什么? 在嵌入式系统中,堆栈指针(SP)的值存储在应用程序(App&…...
如何在 iPhone 上恢复已删除的联系人:简短指南
从 iPhone 中删除联系人相当容易,但如果您不小心删除了错误的联系人或丢失了所有联系人怎么办?这可能是任何智能手机用户都可能发生的最糟糕的噩梦之一。 如何在 iPhone 上恢复已删除的联系人 我个人在我的列表上看到几个用户发布关于他们如何丢失所有联…...
使用Aspose.Words将Word转换为HTML时,字体样式丢失问题及解决方法
使用Aspose.Words将Word转换为HTML时,字体样式丢失问题及解决方法 引言 ✨一、问题描述 📉二、问题分析 🔍三、解决方案 🛠️四、总结 🏁 引言 ✨ 在实际开发中,使用Aspose.Words将Word文档转换为HTML格式…...
更快的图像局部修改与可控生成:Flex.2-preview
Flex.2-preview 文本生成图像扩散模型介绍 一、模型简介 Flex.2-preview 是一种 开源的 80 亿参数文本生成图像扩散模型,具备通用控制和修复支持功能,是 Flex.1alpha 的下一代版本。该模型由社区开发并为社区服务,采用 Apache 2.0 许可证&a…...
汽车制造行业如何在数字化转型中抓住机遇?
近年来,随着新一轮科技革命和产业变革的深入推进,汽车制造行业正迎来一场前所未有的数字化转型浪潮。无论是传统车企还是新势力品牌,都在积极探索如何通过数字化技术提升竞争力、开拓新市场。那么,在这场变革中,汽车制…...
数据可视化 —— 直方图
一、前言 直方图(Histogram)是一种用柱状图形表示数据分布的统计图表,它将数据划分为连续的区间(称为“分箱”或“区间”),统计每个区间内的数据频数(或频率),并用柱形的…...
1、Linux操作系统下,ubuntu22.04版本切换中英文界面
切换中英文界面的方法很多,我也是按照一个能用的方法弄过来并且记录, 1.如果刚开始使用Ubuntu环境,桌面的语言环境为英文,需要安装中文简体的字体包 打开桌面终端,输入 sudo apt install language-pack-zh-hans lan…...
《MySQL 技术内幕-innoDB 存储引擎》笔记
💡 根据 遗忘曲线:如果没有记录和回顾,6天后便会忘记75%的内容 读书笔记正是帮助你记录和回顾的工具,不必拘泥于形式,其核心是:记录、翻看、思考::: 书名MySQL 技术内幕-innoDB 存储引擎作者姜承尧状态已读…...
C++ AVL树的实现
在上一篇博客我们学习了二叉搜索树的实现,现在我们开始手动实现AVL树。 二叉搜索树-CSDN博客 1.AVL树的概念 AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的左右⼦树都是AVL树,…...
多视觉编码器协同与高低分辨率特征融合技术综述
本文主要介绍(论文发表时间:24.03-25.01)在多模态中使用多个视觉编码器如何进行特征融合操作(之所以用多视觉编码器,主要用途在于:有些视觉编码器可能只能提取到部分信息,就想通过另外一个编码器…...
力扣4-最长公共前缀
一.题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""。 示例 1: 输入:strs ["flower","flow","flight"] 输出:"fl"示例 2&…...
贪心算法-860.柠檬水找零-力扣(LeetCode)
一、题目解析 我们需要注意我们是没有初始零钱的,所以当第一个顾客支付10或20时,无法找零此时返回false。 二、算法解析 根据贪心算法的解决方式,我们需要先把解决该问题分解为若干步。 首先对于顾客支付的钱共有三种,5…...
Kubernetes学习笔记-配置Service对接第三方访问
在Kubernetes中配置Service对接第三方访问,可以选择以下方案实现: ExternalName Service(基于DNS别名) 适用场景:外部服务必须有固定域名Service配置文件如下: apiVersion: v1 kind: Service metadata…...
pikachu靶场-敏感信息泄露
一、敏感信息泄露的危害 1. 个人隐私与数据安全 身份盗窃:泄露个人身份信息(如姓名、身份证号、手机号)可被用于诈骗、冒名开户等犯罪活动。账户劫持:暴露用户账号密码、邮箱等凭证,导致社交媒体、银行账户被非法登录。…...
ppt章节页怎么做好看?ppt章节页模板
ppt章节页怎么做好看?ppt章节页怎么排版?ppt章节页模板: PPT章节_模板素材_PPT模板_ppt素材_免抠图片_AiPPTer...
ubuntu扩展逻辑卷并调整文件系统大小步骤
安装好ubuntu如果没有调整磁盘空间,一般默认给你100G的空间,在用完时再调整也还来得及,下面是 ubuntu扩展逻辑卷并调整文件系统大小步骤: 1. 扩展逻辑卷 运行以下命令来扩展逻辑卷 /dev/ubuntu-vg/ubuntu-lv,使其使用卷组中所有未分配的空间ÿ…...
2.脚本文件初识
—>1.Makefile—自动化构建和管理项目的文件见这篇<— 1.编程语言 编程语言分为2类,一类是编译型语言,将源文件经过编译得到可执行文件,该执行文件可以在特定平台上运行,其他平台则不行,因此是不跨平台的编程语…...
FastAPI + Redis Pub/Sub + WebSocket 组合解决方案的详细介绍
以下是对 FastAPI Redis Pub/Sub WebSocket 组合解决方案的详细介绍,涵盖技术原理、实现步骤、协作流程和适用场景。 1. 技术概述 1.1 FastAPI 特性:基于 Python 的现代异步框架,支持 async/await,性能高效,适合高…...
泛型的诗意——深入C++模板的艺术与科学(模版进阶)
前言: 在之前,小编讲述了模版的初阶内容,当时小编讲述了模版的书写,方便之后容器的讲解以及模拟实现,现在小编已经带领各位学习了很多容器,模版初阶的知识已经用的很多了,今天小编讲述一下全新的…...
【极致版】华为云Astro轻应用抽取IoTDA影子设备参数生成表格页面全流程
做份极致详细Astro调取iotda影子设备数据的操作手册,每一步都分成: 要进入哪个界面 点哪个按钮 要填什么内容(样例) 如果出错怎么办 填写示例 完全对应你这个需求:Astro轻应用抽取IoTDA影子设备数据,…...
业务中台与数据中台:企业数字化转型的核心引擎
前言:在当今数字化浪潮下,企业为了提升运营效率、加速创新步伐并更好地适应市场变化,业务中台与数据中台应运而生,成为企业架构中的关键组成部分。本文将深入探讨业务中台和数据中台的简介、发展史、技术流环节以及在实际生产中的…...
前端分页与瀑布流最佳实践笔记 - React Antd 版
前端分页与瀑布流最佳实践笔记 - React Antd 版 1. 分页与瀑布流对比 分页(Pagination)瀑布流(Infinite Scroll)展示方式按页分批加载,有明确页码控件滚动到底部时自动加载更多内容,无明显分页用户控制用…...
【网络原理】从零开始深入理解TCP的各项特性和机制.(三)
上篇介绍了网络原理传输层TCP协议的知识,本篇博客给大家带来的是网络原理剩余的内容, 总体来说,这部分内容没有上两篇文章那么重要,本篇知识有一个印象即可. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分…...
MySQL:13.用户管理
13. 用户管理 如果我们只能使用root用户,这样存在安全隐患。这时,就需要使用MySQL的用户管理。 13.1 用户 13.1.1 用户信息 MySQL中的用户,都存储在系统数据库mysql的user表中 mysql> use mysql; Database changed mysql> select h…...
leetcode0103. 二叉树的锯齿形层序遍历-medium
1 题目:二叉树的锯齿形层序遍历 官方标定难度:中 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行…...
【Go语言】ORM(对象关系映射)库
github.com/jinzhu/gorm 是 Go 语言中一个非常流行的 ORM(对象关系映射)库,用于简化与关系型数据库的交互。以下是关于它的关键信息: 核心特点 全功能 ORM 支持主流数据库:MySQL、PostgreSQL、SQLite、SQL Server 等。…...
Java : GUI
AWT 初始化界面 直接封装起来: panel 的添加 布局 流式布局,控制按钮的位置 东西南北中布局 网格布局 frame.pack();java函数,会自动选择最优的布局 事件监听 给按钮添加 添加文本 画笔 鼠标监听 键盘监听 JDialog”弹窗 默认有关闭事件 标签&#…...
ipa包安装到apple手机上
获ipa包的方式 ipatool 下载appStore的ipa包-CSDN博客 方式一:巨魔商店 原理是利用apple的漏洞,但是有低版本的系统要求 TrollStore - Always Sideload Any IPAs For FreeTrollStore - The ultimate jailbreak app for iOS. Permanently install any …...
JavaScript输出数据的方法
1. console.log() console.log()是最常用的方法之一,用于在浏览器的控制台(Console)中输出信息。这对于调试和查看变量的值非常有用。 console.log("Hello, world!");2. alert() alert()方法会弹出一个带有指定消息和确定按钮的警告…...
操作系统:计算机世界的基石与演进
一、操作系统的本质与核心功能 操作系统如同计算机系统的"总管家",在硬件与应用之间架起关键桥梁。从不同视角观察,其核心功能呈现多维价值: 硬件视角的双重使命: 硬件管理者:通过内存管理、进程调度和设…...
FFmpeg之三 录制音频并保存, API编解码从理论到实战
在学习FFmpeg的时候,想拿demo来练习,官方虽有示例,但更像是工具演示,新手不好掌握,在网上找不到有文章,能给出完整的示例和关键点的分析说明,一步一个错误,慢慢啃过来的,…...
幂等性处理解决方案实战示例
幂等性处理解决方案实战示例 幂等性是指对同一个操作执行一次或多次,产生的结果是相同的。在分布式系统、网络请求和金融交易等场景中,幂等性设计至关重要。下面我将介绍几种常见的幂等性处理方案及其实战示例。 1. 唯一标识符方案 原理:为…...
华为仓颉编程语言的实际用法与使用领域详解
华为仓颉编程语言的实际用法与使用领域详解 一、语言概述与核心特性 华为仓颉编程语言是面向万物智联时代的系统级编程语言,其核心特性包括: 三重内存安全机制:所有权系统 + 引用检查 + 硬件辅助防护零成本抽象:高级语法不牺牲底层性能全场景支持:从嵌入式设备到量子计算…...
JavaEE-多线程实战01
Java 多线程入门:第一个多线程程序 在 Java 中,多线程编程是非常重要的一部分。本篇文章将通过示例,带你快速了解如何创建第一个多线程程序,并深入分析其运行机制。 1. 创建一个线程类并继承 Thread 在 Java 中,我们…...
当AI浏览器和AI搜索替代掉传统搜索份额时,老牌的搜索引擎市场何去何从。
AI搜索与传统搜索优劣势分析 AI搜索优势 理解和处理查询方式更智能:利用自然语言处理(NLP)和机器学习技术,能够更好地理解用户的意图和上下文,处理复杂的问答、长尾问题以及多轮对话,提供更为精准和相关的…...
大模型——Spring.new快速构建AI驱动的定制化商业应用
大模型——Spring.new快速构建AI驱动的定制化商业应用 Spring.new 是一个基于人工智能的在线平台,专注于帮助营销经理和产品经理快速构建定制化工作流和小型应用。它通过自然语言输入,让用户描述需求,自动生成连接 Notion、Airtable、Slack 等工具的工作流或应用,例如将 F…...
django admin 中更新表数据 之后再将数据返回管理界面
在Django中,更新数据库中的数据并将其重新显示在Django Admin界面上通常涉及到几个步骤。这里我将详细说明如何在Django Admin中更新表数据,并确保更新后的数据能够立即在管理界面上显示。 定义模型 首先,确保你的模型(Model&…...