简述C++map容器
pair键值对
std::pair
在很多关联容器(如std::map
、std::multimap
、std::set
、std:multiset
等)中被广泛应用。以std::map
为例,std::map
是一个键值对的容器,其中每个元素都是一个std::pair
,键用于唯一标识元素,值则是与键相关联的数据。
pair是类模板,一般用于表示key/value数据,其实现是结构体。
pair结构模板的定义如下:
template <class T1, class T2>
struct pair
{
T1 first; // 第一个成员,一般表示key。
T2 second; // 第二个成员,一般表示value。
pair(); // 默认构造函数。
pair(const T1 &val1,const T2 &val2); // 有两个参数的构造函数。
pair(const pair<T1,T2> &p); // 拷贝构造函数。
void swap(pair<T1,T2> &p); // 交换两个pair。
};
make_pair函数模板的定义如下:
template <class T1, class T2>
make_pair(const T1 &first,const T2 &second)
{
return pair<T1,T2>(first, second);
}
示例代码:
#include <iostream>
#include<utility>using namespace std;template <class T1, class T2>
struct Pair
{T1 first; // 第一个成员,一般表示key。T2 second; // 第二个成员,一般表示value。Pair() {cout << "调用了有默认的构造函数。\n";}Pair(const T1& val1, const T2& val2) :first(val1), second(val2) {cout << "调用了有两个参数的构造函数。\n";}Pair(const Pair<T1, T2>& p) : first(p.first), second(p.second) {cout << "调用了拷贝构造函数。\n";}
};template <class T1, class T2>
Pair<T1, T2> make_Pair(const T1& first, const T2& second)
{// Pair<T1, T2> p(first, second);// return p; // 返回局部对象。return Pair<T1, T2>(first, second); // 返回临时对象。
}int main()
{pair<int, string> p0;cout << "p0 first=" << p0.first << ",second=" << p0.second << endl;pair<int, string> p1(1, "吴彦祖"); // 两个参数的构造函数。cout << "p1 first=" << p1.first << ",second=" << p1.second << endl;pair<int, string> p2 = p1; // 拷贝构造。cout << "p2 first=" << p2.first << ",second=" << p2.second << endl;p2.swap(p0);cout << "p0 first=" << p0.first << ",second=" << p0.second << endl;cout << "p2 first=" << p2.first << ",second=" << p2.second << endl;auto p3 = make_pair(2, "陈冠希");cout << "键:" << p3.first << ", 值:" << p3.second << endl;
}
运行结果:
map容器
map 容器封装了红黑树(平衡二叉排序树),用于查找。
包含头文件: #include<map>
map容器的元素是pair键值对。
迭代器是双向迭代器。
map类模板的声明:
template <class K, class V, class P = less<K>, class _Alloc = allocator<pair<const K, V >>>
class map : public _Tree<_Tmap_traits< K, V, P, _Alloc, false>>
{
…
}
第一个模板参数K:key的数据类型(pair.first)。
第二个模板参数V:value的数据类型(pair.second)。
第三个模板参数P:排序方法,缺省按key升序。
第四个模板参数_Alloc:分配器,缺省用new和delete。
map提供了双向迭代器。
二叉链表:
struct BTNode
{
pair<K,V> p; // 键值对。
BTNode *parent; // 父节点。
BTNode *lchirld; // 左子树。
BTNode *rchild; // 右子树。
};
一、构造函数
1)map(); // 创建一个空的map容器。
2)map(initializer_list<pair<K,V>> il); // 使用统一初始化列表。
3)map(const map<K,V>& m); // 拷贝构造函数。
4)map(Iterator first, Iterator last); // 用迭代器创建map容器。
5)map(map<K,V>&& m); // 移动构造函数(C++11标准)。
二、特性操作
size_t size() const; // 返回容器的实际大小(已使用的空间)。
bool empty() const; // 判断容器是否为空。
void clear(); // 清空容器。
三、元素操作
V &operator[](K key); // 用给定的key访问元素。
const V &operator[](K key) const; // 用给定的key访问元素,只读。
V &at(K key); // 用给定的key访问元素。
const V &at(K key) const; // 用给定的key访问元素,只读。
注意:
1)[ ]运算符:如果指定键不存在,会向容器中添加新的键值对;如果指定键不存在,则读取或修改容器中指定键的值。
2)at()成员函数:如果指定键不存在,不会向容器中添加新的键值对,而是直接抛出out_of_range 异常。
示例代码:
#include <iostream>
#include <map>
using namespace std;int main()
{map<string, string> m({ { "08","冠希" }, { "03","于晏" }, { "01","彦祖" }, { "07","德华" }, { "05","学友" } });cout << "m[08]=" << m["08"] << endl; // 显示key为08的元素的value。cout << "m[09]=" << m["09"] << endl; // 显示key为09的元素的value。key为09的元素不存在,将添加新的键值对。cout << "m.at(03)=" << m.at("03") << endl;//m.at("099");错误!!m["07"] = "黎明"; // 把key为07的元素的value修改为黎明。m["12"] = "富城"; // 将添加新的键值对。for (auto& val : m)cout << val.first << "," << val.second << " ";cout << endl;
}
运行结果:
四、赋值操作
给已存在的容器赋值,将覆盖容器中原有的内容。
1)map<K,V> &operator=(const map<K,V>& m); // 把容器m赋值给当前容器。
2)map<K,V> &operator=(initializer_list<pair<K,V>> il); // 用统一初始化列表给当前容器赋值。
五、交换操作
void swap(map<K,V>& m); // 把当前容器与m交换。
交换的是树的根结点。
六、比较操作
bool operator == (const map<K,V>& m) const;
bool operator != (const map<K,V>& m) const;
七、查找操作
1)查找键值为key的键值对
在map容器中查找键值为key的键值对,如果成功找到,则返回指向该键值对的迭代器;失败返回end()。
iterator find(const K &key);
const_iterator find(const K &key) const; // 只读。
2)查找键值>=key的键值对
在map容器中查找第一个键值>=key的键值对,成功返回迭代器;失败返回end()。
iterator lower_bound(const K &key);
const_iterator lower_bound(const K &key) const; // 只读。
3)查找键>key的键值对
在map容器中查找第一个键值>key的键值对,成功返回迭代器;失败返回end()。
iterator upper_bound(const K &key);
const_iterator upper_bound(const K &key) const; // 只读。
4)统计键值对的个数
统计map容器中键值为key的键值对的个数。
size_t count(const K &key) const;
八、插入和删除
1)void insert(initializer_list<pair<K,V>> il); // 用统一初始化列表在容器中插入多个元素。
2)pair<iterator,bool> insert(const pair<K,V> &value); // 在容器中插入一个元素,返回值pair:first是已插入元素的迭代器,second是插入结果。
3)void insert(iterator first,iterator last); // 用迭代器插入一个区间的元素。
4)pair<iterator,bool> emplace (...); // 将创建新键值对所需的数据作为参数直接传入,map容器将直接构造元素。返回值pair:first是已插入元素的迭代器,second是插入结果。
例:mm.emplace(piecewise_construct, forward_as_tuple(8), forward_as_tuple("冰冰", 18));
5)iterator emplace_hint (const_iterator pos,...); // 功能与第4)个函数相同,第一个参数提示插入位置,该参数只有参考意义,如果提示的位置是正确的,对性能有提升,如果提示的位置不正确,性能反而略有下降,但是,插入是否成功与该参数元关。该参数常用end()和begin()。成功返回新插入元素的迭代器;如果元素已经存在,则插入失败,返回现有元素的迭代器。
6)size_t erase(const K & key); // 从容器中删除指定key的元素,返回已删除元素的个数。
7)iterator erase(iterator pos); // 用迭代器删除元素,返回下一个有效的迭代器。
8)iterator erase(iterator first,iterator last); // 用迭代器删除一个区间的元素,返回下一个有效的迭代器。
示例代码:
#include <iostream>
#include <map>
using namespace std;int main()
{map<string, string> m({ { "08","冠希" }, { "03","于晏" }, { "01","彦祖" }, { "07","德华" }, { "05","学友" } });// 执行插入操作并检查返回值auto result = m.insert(make_pair("08", "富城"));// result的类型是std::pair<std::map<std::string, std::string>::iterator, bool>// result.first是指向插入位置的迭代器(如果插入成功)或已存在相同键的迭代器(如果插入失败)// result.second是一个布尔值,表示插入是否成功if (result.second) {std::cout << "插入成功" << std::endl;}else {std::cout << "插入失败,键已存在" << std::endl;}for (auto& val : m)cout << val.first << "," << val.second << " ";cout << endl;// 在map容器中查找键值为key的键值对,如果成功找到,则返回指向该键值对的迭代器;失败返回end()。auto it1 = m.find("05");if (it1 != m.end())cout << "查找成功:" << it1->first << "," << it1->second << endl;elsecout << "查找失败。\n";// 在map容器中查找第一个键值 >= key的键值对,成功返回迭代器;失败返回end()。auto it2 = m.lower_bound("05");if (it2 != m.end())cout << "查找成功:" << it2->first << "," << it2->second << endl;elsecout << "查找失败。\n";// 在map容器中查找第一个键值 > key的键值对,成功返回迭代器;失败返回end()。auto it3 = m.upper_bound("05");if (it3 != m.end())cout << "查找成功:" << it3->first << "," << it3->second << endl;elsecout << "查找失败。\n";// 统计map容器中键值为key的键值对的个数。cout << "count(05)=" << m.count("05") << endl; // 返回1。cout << "count(06)=" << m.count("06") << endl; // 返回0。
}
运行结果:
std::map
的 insert
函数在遇到重复键的情况时,会根据其内部实现做出相应处理。通常情况下,它不会覆盖已有的键值对(与 std::unordered_map
不同,std::unordered_map
若插入重复键,会覆盖原来的值),而是会返回一个表示插入结果的对象,这个对象包含了一些信息来表明插入操作是否真正成功插入了新元素。 我们可以接受返回值来确认是否插入成功。
在容器中插入一个元素,返回值pair:first是已插入元素的迭代器,second是插入结果。
按照字符串的字典序比较规则(这也是 std::map
默认用于比较键的规则,因为键是 std::string
类型):
"01"
< "03"
< "05"
< "07"
< "08"
所以查找lower和upper时候会找到学友和德华;
unordered_map容器
unordered(无序)
unordered_map容器封装了哈希表,查找、插入和删除元素时,只需要比较几次key的值。
包含头文件: #include<unordered_map>
unordered_map容器的元素是pair键值对。
unordered_map类模板的声明:
template <class K, class V, class _Hasher = hash<K>, class _Keyeq = equal_to<K>,
class _Alloc = allocator<pair<const K, V>>>
class unordered_map : public _Hash<_Umap_traits<K, V, _Uhash_compare<K, _Hasher, _Keyeq>, _Alloc, false>>
{
…
}
第一个模板参数K:key的数据类型(pair.first)。
第二个模板参数V:value的数据类型(pair.second)。
第三个模板参数_Hasher:哈希函数,默认值为std::hash<K>
第四个模板参数_Keyeq:比较函数,用于判断两个key是否相等,默认值是std::equal_to<K>。
第五个模板参数_Alloc:分配器,缺省用new和delete。
创建std::unordered_map类模板的别名:
template<class K,class V>
using umap = std::unordered_map<K, V>;
std::map
和 std::unordered_map
区别:
1. 底层数据结构
-
std::map
:std::map
的底层数据结构是基于红黑树(Red-Black Tree)实现的。红黑树是一种自平衡二叉搜索树,它在插入和删除节点时会通过一系列的旋转和颜色调整操作来保持树的平衡,使得树的高度始终保持在一个相对较低的水平(最坏情况下为 O(logn),其中n 是树中节点的数量)。- 这种数据结构的优点是能够保证元素按照键的大小顺序进行有序存储,并且在查找、插入和删除操作时,平均时间复杂度都为O(logn) 。例如,当你插入一个新的键值对到
std::map
中时,它会根据键的大小在红黑树中找到合适的位置进行插入,并自动调整树的结构以保持平衡和有序。
-
std::unordered_map
:std::unordered_map
的底层数据结构是基于哈希表(Hash Table)实现的。哈希表通过一个哈希函数将键映射到一个特定的桶(Bucket)中,每个桶可以存储一个或多个键值对。理想情况下,当哈希函数设计得比较好时,查找操作可以在常数时间O(1) 内完成,因为可以通过计算键的哈希值直接定位到对应的桶,然后在桶内进行查找。- 然而,哈希表存在哈希冲突(Hash Collision)的问题,即不同的键可能通过哈希函数计算得到相同的哈希值,从而被映射到同一个桶中。为了解决哈希冲突,通常会采用一些冲突解决策略,如链地址法(将冲突的键值对链接成一个链表存放在同一个桶中)或开放地址法(通过一定的规则在哈希表中重新寻找空闲的位置来存放冲突的键值对)。
2、元素顺序
由于其基于红黑树的实现,std::map
中的元素会按照键的大小顺序自动进行排序。unordered_map
中的元素是无序的,因为哈希表本身并不保证元素的顺序。键值对在哈希表中的存储位置主要取决于键的哈希值以及冲突解决策略。所以,当你遍历 unordered_map
时,元素的出现顺序是不确定的,可能与插入顺序不同,也不一定按照键的任何特定顺序排列。
例如:
#include <iostream>
#include <unordered_map>int main() {std::unordered_map<int, std::string> myUnorderedMap;myUnorderedMap.insert({ 1, "one" });myUnorderedMap.insert({ 3, "three" });myUnorderedMap.insert({ 2, "two" });// 遍历unordered_map输出所有键值对,顺序不确定for (auto it = myUnorderedMap.begin(); it != myUnorderedMap.end(); ++it) {std::cout << "键:" << it->first << ", 值:" << it->second << std::endl;}return 0;
}
这个结果每次输出结果都一样,这其实是有一定概率会出现的情况。虽然理论上 std::unordered_map
的遍历顺序是不确定的,因为它是基于哈希表实现的,元素存储位置取决于键的哈希值以及哈希冲突解决策略等因素。但在实际情况中,如果数据量比较小,而且哈希函数(std::unordered_map
默认的哈希函数或者你自定义的哈希函数,如果有的话)对于你所插入的这些键计算出来的哈希值分布相对比较均匀,没有产生过多的哈希冲突,那么就有可能每次遍历输出的顺序看起来是一样的。
不过,当你插入的数据量逐渐增大,或者插入一些特殊的键值使得哈希冲突情况发生变化时,就很可能会出现不同的遍历顺序了。
例如:
#include <iostream>
#include <unordered_map>
#include<random>
#include<utility>
int main() {std::unordered_map<int, int> myUnorderedMap;for (int i = 1; i < 1000000; i++){std::random_device rd;std::mt19937 gen(rd());// 定义分布范围为1到1000000std::uniform_int_distribution<> dis(1, 1000000);// 生成随机数int randomNumber = dis(gen);myUnorderedMap.insert(std::make_pair(i, randomNumber));}// 遍历unordered_map输出所有键值对,顺序不确定for (auto it = myUnorderedMap.begin(); it != myUnorderedMap.end(); ++it) {std::cout << "键:" << it->first << ", 值:" << it->second << std::endl;}return 0;
}
如果生成的数足够多,顺序是不确定的。
3、查找、插入和删除操作的性能特点
-
std::map
:- 查找、插入和删除操作的平均时间复杂度都是 O(logn)。这意味着在数据量较大时,这些操作的执行时间会随着数据量的增加而以对数级别的速度增长。虽然这种增长速度相对较慢,但相比一些常数时间复杂度的操作(如理想情况下的哈希表查找)还是要慢一些。不过,由于
std::map
能够保持元素的有序性,在一些需要基于顺序进行查找(如查找某个键值对之后的所有键值对)或者进行范围查找(如查找键在某个区间内的所有键值对)的场景下,它具有独特的优势。
- 查找、插入和删除操作的平均时间复杂度都是 O(logn)。这意味着在数据量较大时,这些操作的执行时间会随着数据量的增加而以对数级别的速度增长。虽然这种增长速度相对较慢,但相比一些常数时间复杂度的操作(如理想情况下的哈希表查找)还是要慢一些。不过,由于
-
std::unordered_map
:- 在理想情况下,查找操作的时间复杂度可以达到 O(1),即可以在常数时间内完成查找。这是因为通过哈希函数可以直接定位到键值对可能所在的桶,然后在桶内进行查找。然而,实际情况中,由于哈希冲突的存在,当哈希冲突比较严重时,查找操作可能会退化为链表查找(如果采用链地址法解决冲突),此时查找时间复杂度可能会变为O(n) ,其中 是桶内键值对的数量。插入和删除操作的时间复杂度也类似,在理想情况下为 O(1),但在哈希冲突严重时可能会变差。总体来说,当哈希函数设计得比较好,并且哈希冲突不太严重时,
std::unordered_map
在查找、插入和删除操作上的性能要优于std::map
。
- 在理想情况下,查找操作的时间复杂度可以达到 O(1),即可以在常数时间内完成查找。这是因为通过哈希函数可以直接定位到键值对可能所在的桶,然后在桶内进行查找。然而,实际情况中,由于哈希冲突的存在,当哈希冲突比较严重时,查找操作可能会退化为链表查找(如果采用链地址法解决冲突),此时查找时间复杂度可能会变为O(n) ,其中 是桶内键值对的数量。插入和删除操作的时间复杂度也类似,在理想情况下为 O(1),但在哈希冲突严重时可能会变差。总体来说,当哈希函数设计得比较好,并且哈希冲突不太严重时,
4. 内存占用
-
std::map
:- 由于红黑树需要存储节点之间的指针以及用于保持树平衡的额外信息(如节点的颜色等),相对来说内存占用可能会比
std::unordered_map
要多一些。具体的内存占用情况还会受到键值对类型、树的大小等因素的影响。
- 由于红黑树需要存储节点之间的指针以及用于保持树平衡的额外信息(如节点的颜色等),相对来说内存占用可能会比
-
std::unordered_map
:std::unordered_map
的内存占用主要取决于哈希表的大小(即桶的数量)以及键值对的数量。一般来说,为了减少哈希冲突,哈希表可能需要设置较大的桶数,这可能会导致一定的内存浪费。但总体而言,在数据量较大且哈希冲突不严重的情况下,std::unordered_map
的内存占用可能相对std::map
会有一定的优势。
5、总结
std::map
和 std::unordered_map
各有优缺点,在实际应用中,需要根据具体的应用场景和需求来选择合适的容器。如果需要元素按照键的顺序存储并且对查找、插入和删除操作的平均性能要求不是特别高(能够接受O(logn) 的时间复杂度),那么 std::map
是一个不错的选择;如果更看重查找、插入和删除操作的快速执行(希望在理想情况下达到 O(1) 的时间复杂度),并且不需要元素按照特定顺序排列,那么 std::unordered_map
则更为合适。
相关文章:

简述C++map容器
pair键值对 std::pair在很多关联容器(如std::map、std::multimap、std::set、std:multiset等)中被广泛应用。以std::map为例,std::map是一个键值对的容器,其中每个元素都是一个std::pair,键用于唯一标识元…...

Vue 学习随笔系列十七 -- 表格样式修改
表格样式修改 文章目录 表格样式修改一、表格背景颜色修改1、方法一2、方法二 二、多级表头颜色修改 一、表格背景颜色修改 1、方法一 表格外套一个 div ,修改div的背景色,并将表格背景色设置为透明 参考代码: <template><div cl…...

08 —— Webpack打包图片
【资源模块 | webpack 中文文档 | webpack中文文档 | webpack中文网】https://www.webpackjs.com/guides/asset-modules/?sid_for_share99125_3 Webpack打包图片以8KB为临界值判断 大于8KB的文件:发送一个单独的文件并导出URL地址 小于8KB的文件:导出一…...

01.Django快速入门
一、Django 快速入门 使用最新版本 Django4.2LTS 版本,3 年内不需要更换版本由浅入深讲解,浅显易懂课程大纲全面包含 Django 框架知识点,内容丰富全面细致知识点结合项目实战实现全栈项目应用 Django 官网(文档): https://docs.djangoproject.com/zh-h…...

【大数据学习 | Spark-Core】spark-shell开发
spark的代码分为两种 本地代码在driver端直接解析执行没有后续 集群代码,会在driver端进行解析,然后让多个机器进行集群形式的执行计算 spark-shell --master spark://nn1:7077 --executor-cores 2 --executor-memory 2G sc.textFile("/home/ha…...

Modern Effective C++ Item 14 如果函数不抛出异常请使用noexcept
C11 noexcept关键字用于指定函数不会抛出异常,有助于提高程序的异常安全性,还能够使编译器生成更加高效的代码。 noexcept 是函数接口的一部分 函数是否声明为 noexcept 是接口设计的一部分,客户端代码可能会依赖这一点。如果一个函数被声明…...

cudatoolkit安装(nvcc -V错误版本解决)
CudaToolKit安装(nvcc) cudatoolkit 是 CUDA 开发工具包(CUDA Toolkit) 的核心部分,包含了一系列用于开发和运行 CUDA 应用程序的软件组件。nvcc 是 NVIDIA CUDA 编译器驱动,用于将 CUDA C/C 代码编译成可…...

DTO和VO的区别及使用场景详解
随着互联网的发展,前后端分离的开发模式越来越流行。在前后端数据交互过程中,为了保证数据的安全性和效率,通常会采用 DTO 和 VO 来封装数据。本篇博客将详细介绍 DTO 和 VO 的区别以及使用场景。 大家可能会有个疑问,既然DTO是展…...

百度在下一盘大棋
这两天世界互联网大会在乌镇又召开了。 我看到一条新闻,今年世界互联网大会乌镇峰会发布“2024 年度中国互联网企业创新发展十大典型案例”,百度文心智能体平台入选。 这个智能体平台我最近也有所关注,接下来我就来讲讲它。 百度在下一盘大棋…...

第十六届蓝桥杯模拟赛第二期题解—Java
第十六届蓝桥杯模拟赛/校赛第二期个人题解,有错误的地方欢迎各位大佬指正 问题一(填空题) 【问题描述】 如果一个数 p 是个质数,同时又是整数 a 的约数,则 p 称为 a 的一个质因数。 请问, 2024 的最大的质因数是多少? …...

驱动开发笔记:关于3588GPIO
1.概要 2.内容 1.3588GPIO 关于RK3588的GPIO(General-Purpose Input/Output,通用输入输出引脚),以下是一些关键信息和操作指南: 一、GPIO基本概念 定义:GPIO是嵌入式系统中常见的通信接口,…...

【RK3588 Linux 5.x 内核编程】-内核线程与Mutex
内核线程与Mutex 文章目录 内核线程与Mutex1、Mutex介绍1.1 竞争条件1.2 Mutex特性2、Linux内核中的Mutex2.1 初始化Mutex2.1.1 静态方式初始化2.1.2 动态方式初始化2.2 互斥锁获取2.3 互斥锁释放3、Mutex使用示例4、驱动验证在前面的文章中,介绍了如何Linux内核中的线程,但是…...

【0342】分配并初始化 Proc Signal 共享内存 (1)
1. Proc Signal (procsignal)共享内存 Postgres内核在启动postmaster守护进程时候, 会通过函数 ProcSignalShmemInit() 去为 Proc Signal 分配并初始化指定大小的共享内存空间。整个调用链路如下。 (gdb) bt #0 ProcSignalShmemInit () at procsignal.c:118 #1 0x000000000…...

管家婆财贸ERP BR035.回款利润明细表
最低适用版本: 财贸系列 23.5 插件简要功能说明: 报表统计销售单/销售退货单/销售发票回款情况更多细节描述见下方详细文档插件操作视频: 进销存类定制插件--回款利润明细表 插件详细功能文档: 1. 应用中心增加报表【回款利润明细表】 a. b. 查询条件: ⅰ. 日期区间:…...

数据库MYSQL——表的设计
文章目录 前言三大范式:几种实体间的关系:一对一关系:一对多关系:多对多关系: 前言 之前的博客中我们讲解的是关于数据库的增删改查与约束的基本操作, 是在已经创建数据库,表之上的操作。 在实…...

netstat -tuln | grep 27017(显示所有监听状态的 TCP 和 UDP 端口,并且以数字形式显示地址和端口号)
文章目录 1. 确定占用端口的进程使用 lsof 命令使用 fuser 命令 2. 结束占用端口的进程3. 修改 MongoDB 配置文件4. 检查 MongoDB 日志文件5. 重新启动 MongoDB 服务6. 检查 MongoDB 服务状态总结 [rootlocalhost etc]# netstat -tuln | grep 27017 tcp 0 0 127.0.…...

非线性控制器设计原理
非线性控制器设计原理 非线性控制器设计旨在解决非线性系统的控制问题,克服传统线性控制器在处理非线性现象(如饱和、死区、耦合、时变性等)时的不足。其核心在于利用非线性数学工具和设计方法,使控制系统在非线性条件下具备良好…...

MySQL数据库6——SQL优化
一.SQL优化 1.插入优化 优化1:批量插入 insert into 表名 values(记录1),(记录2),……;优化2:手动提交事务 start transaction; insert into 表名 values(记录1),(记录2); insert into 表名 values(记录1),(记录2); …… commit;优化3:主键顺…...

IDEA配置本地maven
因为idea和maven是没有直接关系的。所以使用idea创建maven工程之前需要将本地的maven配置到idea环境中,这样才可以在idea中创建maven工程。配置方法如下: 1.1 配置本地maven 第一步:关闭当前工程,回到idea主界面找到customize--…...

学习日记_20241123_聚类方法(高斯混合模型)续
前言 提醒: 文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。 其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展…...

SpringMVC——简介及入门
SpringMVC简介 看到SpringMVC这个名字,我们会发现其中包含Spring,那么SpringMVC和Spring之间有怎样的关系呢? SpringMVC隶属于Spring,是Spring技术中的一部分。 那么SpringMVC是用来做什么的呢? 回想web阶段&#x…...

文件操作完成后,为什么要关闭文件
原因包括: 释放系统资源:打开文件时,操作系统会分配资源,如文件描述符或句柄,用于管理文件访问。如果文件保持打开状态,这些资源就不会被释放,可能导致资源耗尽。 确保数据完整性:写…...

vue3+echarts+ant design vue实现进度环形图
1、代码 <div> <!-- 目标环形图 --><div id"main" class"chart_box"> </div><div class"text_target">目标</div> </div>// 目标环形图 const onEcharts () > {// 基于准备好的dom,初…...

使用argo workflow 实现springboot 项目的CI、CD
文章目录 基础镜像制作基础镜像设置镜像源并安装工具git下载和安装 Maven设置环境变量设置工作目录默认命令最终dockerfile 制作ci argo workflow 模版volumeClaimTemplatestemplatesvolumes完整workflow文件 制作cd argo workflow 模版Workflow 结构Templates 定义创建 Kubern…...

C++知识点总结(58):序列型动态规划
动态规划Ⅰ 一、基础1. 意义2. 序列 dp 解法 二、例题1. 最大子段和2. 删数最大子段和(数据强度:pro max)3. 最长上升子序列(数据强度:pro max)4. 3 或 5 的倍数序列5. 数码约数序列 一、基础 1. 意义 动…...

go interface(接口)使用
在 Go 语言中,接口(interface)是一种抽象类型,它定义了一组方法,但是不实现这些方法。接口指定了一个对象的行为,而不关心对象的具体实现。接口使得代码更加灵活和可扩展。 定义接口 接口使用 type 关键字…...

【docker】docker commit 命令 将当前容器的状态保存为一个新的镜像
在Docker容器中安装了许多软件,并希望将当前容器的状态保存为一个新的镜像,可以使用docker commit命令来创建一个新的镜像。以下是如何操作的步骤: 找到容器ID或名称: 首先,需要找到想要保存的容器的ID或名称。可以使用…...

使用 Java 中的 `String.format` 方法格式化字符串
前言 在编程过程中,我们经常需要创建格式化的字符串来满足特定的需求,比如生成用户友好的消息、构建报告或是输出调试信息。Java 提供了一个强大的工具——String.format 方法,它可以帮助我们轻松地完成这些任务。 String.format 方法简介 …...

图论最短路(floyed+ford)
Floyd 算法简介 Floyd 算法(也称为 Floyd-Warshall 算法)是一种动态规划算法,用于解决所有节点对之间的最短路径问题。它可以同时处理加权有向图和无向图,包括存在负权边的情况(只要没有负权环)。 核心思…...

BERT的中文问答系统39
实现当用户在GUI中输入问题(例如“刘邦”)且输出的答案被标记为不正确时,自动从百度百科中搜索相关内容并显示在GUI中的功能,我们需要对现有的代码进行一些修改。以下是完整的代码,包括对XihuaChatbotGUI类的修改以及新…...