C++ STL中 set 和 map 的区别

目录

1.引言

2.set

3.map

4.共同点

5.不同点

6.高级用法与注意事项

7.总结


1.引言

        在C++的标准模板库(STL)中,setmap是两种常用的容器,它们提供了不同的功能和使用场景。尽管它们底层都基于红黑树实现,有许多相似之处,但在具体用途和接口上却有明显的区别。本文将详细探讨setmap的差异,帮助大家在实际编程中更好地选择和使用它们。

2.set

set是一种关联容器,主要用于存储唯一的、已排序的元素。在set中,每个元素都是唯一的,且set会自动根据元素的值进行排序(默认按升序)。试图插入重复元素将被忽略,这是set设计的初衷。确保插入前元素的唯一性,或利用此特性进行去重或排序。set内部实现通常采用红黑树,因此其插入、删除和查找操作的时间复杂度为O(log n)。

#include <set>
#include <iostream>

int main() {
    std::set<int> mySet = {3, 1, 4, 1, 5, 9};  // 自动排序并去重
    for (int num : mySet) {
        std::cout << num << " ";
    }

    mySet.insert(10); // 成功插入
    mySet.insert(10); // 重复,不会插入

    return 0;
}

3.map

map也是一种关联容器,但它存储的是键值对(key-value pairs)。在map中,每个键都是唯一的,且map会根据键的值自动排序(默认按升序)。值可以重复。键用于排序和查找,值则存储实际数据。尝试插入已存在的键会导致插入失败,而不是覆盖原有值。若需覆盖,请先检查键是否存在,再决定插入或更新。set类似,map的内部实现也基于红黑树,因此其插入、删除和查找操作的时间复杂度同样为O(log n)。

#include <iostream>
#include <map>
using namespace std;
 
#include <string>
 
void TestMap()
{
	map<string, string> m;
	// 向map中插入元素的方式:
	// 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
	m.insert(pair<string, string>("peach", "桃子"));
	// 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
	m.insert(make_pair("banan", "香蕉"));
 
	// 借用operator[]向map中插入元素
	/*
	operator[]的原理是:
	用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
	如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
	如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
	operator[]函数最后将insert返回值键值对中的value返回
	 */
	 // 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引用结果,
	m["apple"] = "苹果";
	// key不存在时抛异常
	//m.at("waterme") = "水蜜桃";
	cout << m.size() << endl;
	// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
	for (auto& e : m)
		cout << e.first << "--->" << e.second << endl; // 遍历键值对
	cout << endl;
	// map中的键值对key一定是唯一的,如果key存在将插入失败
	auto ret = m.insert(make_pair("peach", "桃色"));
	if (ret.second)
		cout << "<peach, 桃色>不在map中, 已经插入" << endl;
	else
		cout << "键值为peach的元素已经存在:" << ret.first->first << "--->" <<
		ret.first->second << " 插入失败" << endl;
	// 删除key为"apple"的元素
	m.erase("apple");
	if (1 == m.count("apple"))
		cout << "apple还在" << endl;
	else
		cout << "apple被吃了" << endl;
}
 
int main()
{
	TestMap();
	return 0;
}

4.共同点

  1. 有序性set 和 map 都是有序的容器,它们的元素会根据一定的排序准则(通常是 < 运算符,但也可以是自定义的比较函数或函数对象)自动排序。
  2. 唯一性set 和 map 中的元素都是唯一的,不允许有重复的元素。
  3. 自动平衡:它们的底层实现通常基于红黑树(Red-Black Tree),因此插入、删除和查找操作的时间复杂度都是 O(log n)。

5.不同点

  1. 存储的元素类型
    • set 存储的是单个元素,每个元素都是唯一的。
    • map 存储的是键值对(key-value pairs),其中键(key)是唯一的,但值(value)可以重复。
  2. 元素访问方式
    • set 中的元素只能通过迭代器访问,或者通过 find 方法查找。
    • map 中的元素可以通过键(key)直接访问,例如 map[key],或者通过 find 方法查找。
  3. 迭代器类型
    • set 的迭代器指向的是单个元素。
    • map 的迭代器指向的是键值对(pair<const Key, T>)。
  4. 适用场景
    • set 适用于需要存储唯一元素并且需要排序的场景,比如去重和排序后的集合。
    • map 适用于需要存储键值对并且需要根据键进行快速查找、插入和删除的场景,比如字典或缓存。
  5. 额外功能

     1.set:提供如lower_boundupper_bound等成员函数,方便进行范围查找;

                            支持一些集合运算,如并集、交集、差集等(通过STL算法库实现)。    

     2.mapmap的键可以是任意类型,只要支持<操作符(或提供自定义比较函数)。

                            map的值可以是任意类型,甚至是另一个mapset

     6.性能对比

        在大多数情况下,setmap的性能是相近的,因为它们的底层实现都是红黑树。然而,由于map需要额外存储键和值,因此在内存占用上可能会比set更高一些。

6.高级用法与注意事项

1. 自定义排序

我们可以通过提供自定义的比较函数或函数对象来改变setmap的默认排序行为。

set:

#include <set>
#include <iostream>
#include <functional>

struct MyStruct {
    int id;
    std::string name;
};

// 自定义比较函数
bool compareMyStruct(const MyStruct& a, const MyStruct& b) {
    return a.id < b.id;
}

int main() {
    std::set<MyStruct, std::function<bool(const MyStruct&, const MyStruct&)>> mySet(compareMyStruct);
    mySet.insert({1, "Alice"});
    mySet.insert({2, "Bob"});
    mySet.insert({3, "Charlie"});

    for (const auto& item : mySet) {
        std::cout << item.id << ": " << item.name << std::endl;
    }
    return 0;
}

map:

#include <map>
#include <iostream>
#include <string>
#include <functional>

// 自定义比较函数
struct CustomCompare {
    bool operator()(const std::string& a, const std::string& b) const {
        return a > b;  // 降序排序
    }
};

int main() {
    std::map<std::string, int, CustomCompare> myMap;
    myMap["apple"] = 1;
    myMap["banana"] = 2;
    myMap["cherry"] = 3;

    for (const auto& pair : myMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    return 0;
}

2.多线程安全

setmap都不是线程安全的,如果在多线程环境中使用,需要手动进行同步。

3.迭代器失效

setmap中,插入不会使迭代器失效,但删除操作会使指向删除元素的迭代器失效。因此,在进行删除操作时,需要特别注意迭代器的有效性。

7.总结

setmap是C++ STL中两种重要的关联容器,它们各自有不同的特点和适用场景。set主要用于存储唯一且排序的元素,而map则用于存储键值对,并根据键进行排序。尽管它们在底层实现上有许多相似之处,但在接口和使用上却有明显的区别。通过理解这些区别,我们可以更好地选择和使用它们,以满足不同的编程需求。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值