set和map

目录

一、概述

1.set常用成员函数

insert插入

erase删除 

​编辑 lower_bound和upper_bound

equal_range 

2.map常用的成员函数

operator[ ]重载

3.set以及map的返回值

二、map和set的模拟实现

第一个难点

第二个难点

 三、迭代器设计

 operator !=和operator ==

operaor->和operator*

 operator++,operator--

后置++和后置--

begin()和end()迭代器设计

const迭代器设计

map和set的insert函数封装

map的operator[ ]

析构及拷贝构造

完整代码如下

RBtree.h文件代码

myset.h文件代码

mymap.h的代码


一、概述

map和set与以往容器不同的是它是关联式容器,而vector、list、deque是序列式容器,序列式容器是以线性排列的方式来存储数据的,元素在容器中的位置与插入顺序有关,其实底层是顺序表和链表。而map和set底层是二叉搜索树,更深入点讲是红黑树,元素在容器中的位置与插入顺序无关,因为有可能经过红黑树旋转改变原先的位置。

与别的容器不同的是set会自动排序和去重

 set值得注意的第二点是set不支持修改,只能删后重新插入。因为底层是红黑色,直接进行修改会直接破坏红黑树和二叉搜索树的规则

 比如下面的二叉搜索树

把100直接改为20就破坏了左树都比右树值小,右树值都比左树值大。红黑树调整包括二叉搜索树的调整都是从局部子树出发一步一步往上调整影响全局(也就是插入一个节点调整一部分子树),很少整体进行二叉搜索树或者红黑树规则调整,因为代价太大了,有可能要动几乎所有节点。

二叉搜索树随意插入的例子

map需要注意的点是它里面不是存单独的值了,而是以键值对的方式进行存储(key:"键",value:"值"),键和值可以是不同的类型,值可以修改,但是键是不能修改的

但是map的两个参数在插入的时候是不能直接用相对应的值进行插入的,比如map<string,int> ret

ret.insert("青菜",1) 

 为什么上面的例子会报错呢,这是因为map的insert插入的是pair,而不是直接的两个值,直接插入两个值是没有关联性的

value_type其实是pair类型的重命名

value_type的具体情况

pair是一个模版类,用来存储一对值,允许将两个相关的值组合在一个对象中,pair的两个值可以是不同的类型

pair模版类

为什么map采用pair这种模版类来中转存储键值对呢,因为通过pair可以很容易将key和value联系起来,pair的两个值可以通过first和secod来进行访问

访问pair里的元素

所以在map中insert会写成insert({"青菜",5}) ,里面的花括号其实是走了一个隐式类型转换,隐式类型转换成了pair类型

隐式类型转换进行insert

同时也可以通过make_pair来进行插入

make_pair进行insert

 那么make_pair是什么呢,make_pair其实就是构造pair的一个函数

make_pair的具体情况

map其实也是有序的,只不过是按照key的字典序进行排序的,默认情况下是不会按照value进行排序的。

map不能插入相同key的元素(如果有两个相同的key值的元素会自动忽略最后一个),但是可以插入不同key,相同value的值

二、set、map常用成员函数介绍

1.set常用成员函数

insert插入

第一种直接给值进行插入,这种也是最常用的insert

第二种在指定迭代器位置插入

这样通过insert在指定位置插入,会自动调整位置,但是通过解引用修改迭代器的指向元素的值是不支持的

 

 第三种插入一段迭代器区间的值

这个也会自动调整位置的值,但是输出运行可能会有点慢

erase删除 

第一种直接给一个迭代器位置进行删除

 第二种直接给一个值进行删除,这种也是最常用的删除

第三种迭代器区间删除

 lower_bound和upper_bound

lower_bound返回的是第一个大于等于指定val值的迭代器
iterator lower_bound (const value_type& val) const;

upper_bound返回的是第一个大于val的值

iterator upper_bound (const value_type& val) const;

  lower_bound和upper_bound究竟有什么用呢,这两个组合在一起可以构造成一段左闭右开的迭代器区间,可以直接通过erase的迭起器区间删除直接删除一段区间的值

equal_range 

 equal_range函数返回的是一个pair,pair当中first值是val的 lower_bound,而second返回的是val的upper_bound

2.map常用的成员函数

operator[ ]重载

 map的成员函数与set以及别的成员函数都比较类似

唯一指定注意的是map重载了operator[  ]这个重载与数组里的[  ]通过下标访问下标对应的值不同,map里的operator[  ]是通过key值来返回对应的value值

那么operator[ ]返回值是什么呢,来看看库里是怎么写的

mapped_type& operator[] (const key_type& k);

也就是说插入之后返回的是当前插入key值对应的value,所以可以通过operator[ ]完成修改,插入key,插入key和value三种不同的操作,如果当前的key存在的话就是修改操作,如果不存在的话就是修改操作

比如修改苹果的数量为100

插入操作,插入香蕉

如果你访问一个不存在的key值,并且不知道value值的话,其实是返回默认函数构造的结果,int类型返回0,string类型返回" " 

3.set以及map的返回值

set的insert

map的insert

从上面两张图可以看出来set和map的insert都返回了pair<iterator,bool>,那么这个pair究竟是什么呢?为什么不返回bool呢?

std::pair<iterator, bool> 的第一个元素是一个迭代器,指向插入元素的位置或者已存在元素的位置,这样可以让用户知道插入操作的结果是成功的还是失败的,还可以知道这个元素具体在容器中的位置。如果只返回一个 bool 值,用户就无法获得这样的位置信息。

二、map和set的模拟实现

第一个难点

key值存储和key value键值对存储

怎么在一份红黑树代码的基础上做成key值存储节点和key value键值队存储节点两种不同的方式

难点就在于set里的成员只有key,而map的成员是pair键值对,红黑树的节点要么只能是key,要么就是key value键值对,怎么在一份红黑树代码的情况下能做成两种不同的效果呢?

先来看看源代码

set里的情况

map里的情况

红黑色的模版参数情况

 其实截图到这里可以看出来前两个模版参数是关键了,再具体看看红黑色存节点数据的模版参数

从上述截图中可以总结出一个点,set也传了两个参数过来,Key_tybe和value_type,但是从typedef里可以看出来,Key_type和value_type其实都是key值。而map需要正常传key值和value值, 但是实际上map传的value_type是typedef  pair<const key,T>得来的,往上看T其实typedef成了data_type所以其实这个T类型就是value的类型,所以map实际传的value_type是一个 pair<key,value>键值队

而在红黑树里存节点数据的关键实际就是value_type类型,当set对象实例化的时候,传给红黑树的value_type类型的是单独的key类型,而map对象实例化的时候传给红黑色的value_type是一个键值对形式pair<key,value>,这样就做到了一份代码但是存储的节点不同,实际上就是利用模版传参的类型不同来进行分割

这一部份代码在map和set里只需要定义成员变量就可以了

set里注意前两个参数都得是Key的类型

map第一个模版参数依旧是key值的类型,而第二个模版参数要改成key value键值队 

对于红黑树代码只需要改局部,把具体节点的类型改成value的类型V就可以了

 

第二个难点

在红黑树当中是有节点值的比较的,如果是set那么就不需要修改直接进行比较就可以了,如果是map那么就比较麻烦

先来看看pair是怎么比较的

两个pair之间进行比较

从上图可以看出来pair是先key值进行比较,然后再进行value值的比较,但是库里的map实际上只会进行key的比较,value值压根不会进行比较。

如果是如下图红黑树节点的源代码直接进行比较的话,data和value此时由map实例化实际上都是pair,所以一点也不改的话就会使比较出现错误

红黑树的局部代码

解决办法是通过仿函数来改变比较的对象,然后利用泛型编程来进行处理,代码如下

 set的代码

set的比较不需要进行改变,所以仿函数原模原样传回去就可以了

map传key值过去

因为各自传过的KeyOfValue类型不同,其内部仿函数不同,所以比较的结果和方式也会发生改变,在红黑树里可以实例化KeyOfValue的对象,然后把要比较的数据通过KeyOfValue仿函数来拿到正确进行比较的数据

局部代码对比如下

 三、迭代器设计

依旧是将节点指针封装成迭代器

 operator !=和operator ==

	bool operator !=(const self& tamp)
	{
		return cur != tamp->cur;
	}

	bool operator ==(const self& tamp)
	{
		return cur == tamp->cur;
	}

operaor->和operator*

 operator++,operator--

先画一个树图来举个例子

值得注意的是二叉搜索树有序输出是按照中序遍历的规则(左、根、右)来进行输出的,所以++和--都要符合中序的规则,比如当前在80那个位置进行++,按照下一步就到90了

在++的情况下,如果当前的节点的右孩子存在的话,就得找右子树的最左节点,当前节点进行不是直接访问右孩子节点,因为所有的子树也都得符合左、根、右的规则,所以实际上去找右子树当中按照中序遍历规则的第一个节点(因为右子树也得遵守左、根、右的规则,所以右子树的最左节点才是第一个访问的节点)。比如上图的二叉搜索树规则下我当前访问的是120,下一个访问的是140,而不是直接是120的孩子节点160,因为120的子树也得符合中序遍历规则,所以要找以160为根节点的最左节点。

如果当前节点的右孩子节点不存在的话,分为两种情况,依旧是以上图为例子,如果当前节点是70的话,70没有右孩子节点,所以70访问完了就代表以70为根节点的子树访问完了,再进行++的话就得判断70是其parent节点80的哪个孩子,如果是左孩子的话那么下一步就直接访问parent节点就可以了。右孩子不存在其实就说明以当前节点为根节点的子树的所有结点都访问完了。

第二种右孩子节点不存在的情况是当前节点是90,90与上面的例子相反,90是80的右孩子节点。90的右孩子节点不存在,其实说明以90为根节点的子树全部访问完了,而以90为根节点的子树是80的右子树,按照中序左、根、右的规则说明以80为根节点的整颗树都访问完了(因为右子树或者右节点是最后访问的),而80完了后80是其parent节点100的左孩子,所以此时又变成了第一种情况了直接访问parent节点就可以了。  最最后的情况是当前访问的节点是165,是整颗树的最右节点,165访问完了,它是160的右孩子,所以代表以160为根节点的子树都访问完了,而160又是120的右孩子,所以就说明以160为根节点的树全都访问完了

代码如下

	self& operator++()
	{
		// 如果右子树存在,找到右子树的最左节点
		if (cur->right)
		{
			Node* rightnode = cur->right;
			while (rightnode->left)
			{
				rightnode = rightnode->left;
			}
			cur = rightnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}

operator--与operator++相反,++是按照左根右来进行辅助的,而--是倒过来按照右根左来进行处理的。所以第一步先判断左孩子是不是为空,如果左孩子不为空,那么就去找左子树中的最右节点。

如果是左孩子为空,那么就有两种情况了

第一种情况当前节点是parnet节点的右节点,那么--就意外着下一个要访问就是parnet节点。

如果当前节点是parent节点的左节点,那就意味着以当前节点为根节点的这颗子树的节点已经全访问完了,所以下一步是一直往上找cur节点是parent节点的右节点的情况,构成第一种情况再进行处理,或者直接全处理完了节点,最后为空

除此之外--还需要考虑一个情况,end迭代器是不存具体数据的,所以一般都会定义成空,但是如果需要--来倒着遍历这颗树的话,就得找到最后一个节点,其实也就是这颗树的最右节点

代码如下

self& operator--()
{
	if (cur == nullptr)
	{
		Node* tamp = _root;
		while (tamp->right)
		{
			tamp = tamp->right;
		}
		cur = tamp;
	}
	// 如果左子树存在,找到左子树的最右节点
	else if (cur->left)
	{
		Node* leftnode = cur->left;
		while (leftnode->right)
		{
			leftnode = leftnode->right;
		}
		cur = leftnode;
	}
	else
	{
		// 否则,向上回溯到第一个父节点
		Node* parent = cur->_parent;
		while (parent && cur == parent->left)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		cur = parent;
	}
	return *this;
}

后置++和后置--

后置++和后置--与前置的相比后置的是先使用数据然后进行++,所以要用一个变量来存储原来的数值,值得注意的是此时不能再使用引用了,因为这个变量出来作用域就直接销毁了


	self operator++(int)
	{
		self temp = *this; // 创建当前对象的副本
		++(*this); // 调用前置++
		return temp; // 返回副本
	}

	self operator--(int)
	{
		self temp = *this; // 创建当前对象的副本
		--(*this); // 调用前置--
		return temp; // 返回副本
	}

也可以这样写

self operator++(int)
{
	self temp = *this; // 创建当前对象的副本
	// 如果右子树存在,找到右子树的最左节点
	if (cur->right)
	{
		Node* rightnode = cur->right;
		while (rightnode->left)
		{
			rightnode = rightnode->left;
		}
		cur = rightnode;
	}
	else
	{
		// 否则,向上回溯到第一个父节点
		Node* parent = cur->_parent;
		while (parent && cur == parent->right)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		cur = parent;
	}
	return temp; // 返回副本
}

self operator--(int)
{
	self temp = *this; // 创建当前对象的副本
	if (cur == nullptr)
	{
		Node* tamp = _root;
		while (tamp->right)
		{
			tamp = tamp->right;
		}
		cur = tamp;
	}
	// 如果左子树存在,找到左子树的最右节点
	else if (cur->left)
	{
		Node* leftnode = cur->left;
		while (leftnode->right)
		{
			leftnode = leftnode->right;
		}
		cur = leftnode;
	}
	else
	{
		// 否则,向上回溯到第一个父节点
		Node* parent = cur->_parent;
		while (parent && cur == parent->left)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		cur = parent;
	}
	return temp; // 返回副本
}

 

begin()和end()迭代器设计

begin()迭代器是找第一个元素,在树里面如果要有序的话一般是按照中序遍历的规则(左、根右),所以begin()直接找最左边的节点就可以了。而end()迭代器不存实际的元素,所以直接用空节点构造迭代器就可以了

Iterator begin()
{
	Node* cur = root;
	while (cur && cur->left)
	{
		cur = cur->left;
	}
	return Iterator(cur, root);
}
Iterator end()
{
	return Iterator(nullptr, root);
}

const迭代器设计

set要注意的是无论是什么迭代器都不允许修改,而map迭代器是不允许修改key但是可以修改value值,那么怎么处理呢?在传模版参数的时候直接用const限制不能修改的类型就可以了,值得注意的是红黑树的节点和set以及map传过来的第二个模版参数有关,所以只需要限制第二个模版参数类型就可以了

比如set里重命名迭代器

为什么这里要加个typename呢,这是因为通过类的作用域取的可不一定是类型,static变量和static函数都可以直接通过类作用域取到,而typedef只能用于类型,编译器不敢直接typedef(基本上会报错),所以typename就是告诉编译器我要取的东西是类型,而不是别的东西

map里重命名迭代器

它不允许key值进行修改,所以在pair当中的第一个类型参数给const就可以了

 

在RBtree里的const迭代器与之前讲得的list等容器的操作一样,正常的iterator迭代器就传没有const的类型,而const迭代器传cosnt类型,迭代器代码里用ref和btf进行接收

完整红黑树迭代器代码如下

template<class V, class Ref, class Btf>
class RBiterator
{
public:
	typedef RBtreeNode<V> Node;
	typedef RBiterator<V, Ref, Btf> self;
	RBiterator(Node* tamp, Node* root) :cur(tamp), _root(root) {};//构造函数

	Ref operator*()
	{
		return cur->data;
	}

	Btf operator->()
	{
		return &(cur->data);
	}




	// 重载前置++操作符,向后迭代
	self& operator++()
	{
		// 如果右子树存在,找到右子树的最左节点
		if (cur->right)
		{
			Node* rightnode = cur->right;
			while (rightnode->left)
			{
				rightnode = rightnode->left;
			}
			cur = rightnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}


	self& operator--()
	{
		if (cur == nullptr)
		{
			Node* tamp = _root;
			while (tamp->right)
			{
				tamp = tamp->right;
			}
			cur = tamp;
		}
		// 如果左子树存在,找到左子树的最右节点
		else if (cur->left)
		{
			Node* leftnode = cur->left;
			while (leftnode->right)
			{
				leftnode = leftnode->right;
			}
			cur = leftnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}

	self operator++(int)
	{
		self temp = *this; // 创建当前对象的副本
		++(*this); // 调用前置++
		return temp; // 返回副本
	}

	self operator--(int)
	{
		self temp = *this; // 创建当前对象的副本
		--(*this); // 调用前置--
		return temp; // 返回副本
	}



	bool operator !=(const self& tamp)
	{
		return cur != tamp.cur;
	}

	bool operator ==(const self& tamp)
	{
		return cur == tamp.cur;
	}
private:
	Node* cur;
	Node* _root;
};

map和set的insert函数封装

set和map的insert函数返回值都是pair<iterator,bool>,而我的原版红黑树的插入返回值是bool,所以最好需要修改成同样的类型,方便操作。值得注意的是最后返回的时候不能直接用cur构造pair进行返回,因为cur在红黑色进行调整的时候是回往上进行调整的,已经不是了刚刚插入的新节点的位置了,所以要在调整之前先用一个指针变量保存这个新节点的位置

完整insert代码如下

pair<Iterator, bool> insert(const V& value)//修改插入类型
{
	// 如果树为空,则插入新节点作为根节点  
	if (root == nullptr)
	{
		root = new Node(value); // 创建新节点  
		root->col = black;    // 根节点总是黑色  
		root->_parent = nullptr; // 根节点没有父节点  
	}

	// 初始化当前节点和父节点  
	Node* cur = root;
	Node* parent = cur->_parent;
	KeyOfValue KV;//实例化KeyOfValue
	// 查找插入位置  
	while (cur)
	{
		if (KV(value) > KV(cur->data)) // 通过仿函数来修改比较规则
		{
			parent = cur;
			cur = cur->right;
		}
		else if (KV(value) < KV(cur->data))
		{
			parent = cur;
			cur = cur->left;
		}
		else
		{
			return { Iterator(cur,root),false }; // 插入失败  
		}
	}

	// 创建新节点并插入  
	cur = new Node(value); // 创建新节点  
	cur->col = red;      // 新节点初始为红色  

	Node* newnode = cur;//在调整前记录新节点位置

	// 将新节点链接到父节点  
	if (KV(cur->data) > KV(parent->data)) // 新节点键大于父节点键,插入右边  
	{
		parent->right = cur;   // 将新节点链接到父节点的右子节点  
		cur->_parent = parent; // 设置新节点的父节点  
	}
	else // 新节点键小于父节点键,插入左边  
	{
		parent->left = cur;    // 将新节点链接到父节点的左子节点  
		cur->_parent = parent; // 设置新节点的父节点  
	}

	// 根据红黑树性质,调整红黑树  
	while (parent && parent->col == red) // 检查父节点是否为红色  
	{
		Node* grandfather = parent->_parent; // 获取祖父节点  

		if (parent == grandfather->left) // 如果父节点是祖父节点的左子节点  
		{
			Node* uncle = grandfather->right; // 叔叔节点  
			if (uncle && uncle->col == red) // 再次检查叔叔节点颜色  
			{
				// 情况1:叔叔节点也为红色  
				uncle->col = parent->col = black; // 将父节点和叔叔节点设置为黑色  
				grandfather->col = red;           // 将祖父节点设置为红色  
				cur = grandfather;                // 继续向上调整  
				parent = cur->_parent;            // 更新父节点  
			}
			else // 叔叔节点为黑色  
			{
				if (cur == parent->left) // 情况2:新插入节点是父节点的左子节点  
				{
					RotateR(grandfather); // 旋转祖父节点向右  
					parent->col = black;  // 设置父节点为黑色  
					grandfather->col = red; // 设置祖父节点为红色  
				}
				else // 情况3:新插入节点是父节点的右子节点  
				{
					RotateL(parent);          // 先左旋转父节点  
					RotateR(grandfather);     // 再右旋转祖父节点  
					cur->col = black;         // 设置新节点为黑色  
					grandfather->col = red;   // 设置祖父节点为红色  
				}
				break; // 调整完成,退出循环  
			}
		}
		else // 如果父节点是祖父节点的右子节点  
		{
			Node* uncle = grandfather->left; // 叔叔节点  
			if (uncle && uncle->col == red) // 再次检查叔叔节点颜色  
			{
				// 情况1:叔叔节点也为红色  
				uncle->col = parent->col = black; // 将父节点和叔叔节点设置为黑色  
				grandfather->col = red;           // 将祖父节点设置为红色  
				cur = grandfather;                // 继续向上调整  
				parent = cur->_parent;            // 更新父节点  
			}
			else // 叔叔节点为黑色  
			{
				if (cur == parent->right) // 情况2:新插入节点是父节点的右子节点  
				{
					RotateL(grandfather); // 旋转祖父节点向左  
					parent->col = black;  // 设置父节点为黑色  
					grandfather->col = cur->col = red; // 设置祖父节点为红色,保持新节点为红色  
				}
				else // 情况3:新插入节点是父节点的左子节点  
				{
					RotateR(parent);          // 先右旋转父节点  
					RotateL(grandfather);     // 再左旋转祖父节点  
					grandfather->col = red;   // 设置祖父节点为红色  
					cur->col = black;         // 设置新节点为黑色  
				}
				break; // 调整完成,退出循环  
			}
		}
	}

	// 确保根节点始终为黑色  
	root->col = black;
	return { Iterator(newnode,root),false };  //返回新节点位置,而不是cur的位置 
}

map的operator[ ]

map的[ ]需要借助insert来进行处理,insert返回的是pair<itertaor,bool>类型,不仅可以判断是否插入成功,而且可以利用iterator来改变值,[ ]改变的是key对应的value值,所以直接拿到iterator的second就可以修改value值了。

 V& operator[](const K&key)
 {
     pair<iterator, bool> ret=insert(make_pair(key,V()));
     return ret.first->second;
 }

析构及拷贝构造

 // 拷贝构造函数
    RBtree(const RBtree& t)
    {
        root = copy(t.root, nullptr);
    }

    // 拷贝函数,递归深拷贝所有节点
    Node* copy(const Node* cur, Node* parent)
    {
        if (cur == nullptr)
            return nullptr;

        // 创建新节点
        Node* newnode = new Node(cur->data);
        newnode->col = cur->col;     // 拷贝节点颜色
        newnode->_parent = parent;   // 设置新节点的父节点

        // 递归拷贝左右子树
        newnode->left = copy(cur->left, newnode);
        newnode->right = copy(cur->right, newnode);

        return newnode;
    }

    // 递归删除所有节点
    void deletenode(Node* cur)
    {
        if (cur == nullptr)
            return;

        deletenode(cur->left);
        deletenode(cur->right);
        delete cur;
    }

    // 析构函数
    ~RBtree()
    {
        deletenode(root);
    }

完整代码如下

RBtree.h文件代码

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

enum colour
{
	black,
	red
};
template<class V>
struct RBtreeNode
{
	RBtreeNode<V>* left;
	RBtreeNode<V>* right;
	RBtreeNode<V>* _parent;
	V data;
	colour col = red;
	RBtreeNode(const V& key) :left(nullptr), right(nullptr), _parent(nullptr), data(key) {};
};


template<class V, class Ref, class Btf>
class RBiterator
{
public:
	typedef RBtreeNode<V> Node;
	typedef RBiterator<V, Ref, Btf> self;
	RBiterator(Node* tamp, Node* root) :cur(tamp), _root(root) {};//构造函数

	Ref operator*()
	{
		return cur->data;
	}

	Btf operator->()
	{
		return &(cur->data);
	}




	// 重载前置++操作符,向后迭代
	self& operator++()
	{
		// 如果右子树存在,找到右子树的最左节点
		if (cur->right)
		{
			Node* rightnode = cur->right;
			while (rightnode->left)
			{
				rightnode = rightnode->left;
			}
			cur = rightnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}


	self& operator--()
	{
		if (cur == nullptr)
		{
			Node* tamp = _root;
			while (tamp->right)
			{
				tamp = tamp->right;
			}
			cur = tamp;
		}
		// 如果左子树存在,找到左子树的最右节点
		else if (cur->left)
		{
			Node* leftnode = cur->left;
			while (leftnode->right)
			{
				leftnode = leftnode->right;
			}
			cur = leftnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}

	self operator++(int)
	{
		self temp = *this; // 创建当前对象的副本
		++(*this); // 调用前置++
		return temp; // 返回副本
	}

	self operator--(int)
	{
		self temp = *this; // 创建当前对象的副本
		--(*this); // 调用前置--
		return temp; // 返回副本
	}



	bool operator !=(const self& tamp)
	{
		return cur != tamp.cur;
	}

	bool operator ==(const self& tamp)
	{
		return cur == tamp.cur;
	}
private:
	Node* cur;
	Node* _root;
};



template<class K, class V, class KeyOfValue>
class RBtree
{
public:
	typedef RBtreeNode<V> Node;
	typedef  RBiterator<V, V&, V*> Iterator;
	typedef  RBiterator<V, const V&, const V*> constiterator;

	Iterator begin()
	{
		Node* cur = root;
		while (cur && cur->left)
		{
			cur = cur->left;
		}
		return Iterator(cur, root);
	}
	Iterator end()
	{
		return Iterator(nullptr, root);
	}
	Iterator begin() const
	{
		Node* cur = root;
		while (cur && cur->left)
		{
			cur = cur->left;
		}
		return constiterator(cur, root);
	}
	Iterator end() const
	{
		return constiterator(nullptr, root);
	}


	pair<Iterator, bool> insert(const V& value)//修改插入类型
	{
		// 如果树为空,则插入新节点作为根节点  
		if (root == nullptr)
		{
			root = new Node(value); // 创建新节点  
			root->col = black;    // 根节点总是黑色  
			root->_parent = nullptr; // 根节点没有父节点  
		}

		// 初始化当前节点和父节点  
		Node* cur = root;
		Node* parent = cur->_parent;
		KeyOfValue KV;//实例化KeyOfValue
		// 查找插入位置  
		while (cur)
		{
			if (KV(value) > KV(cur->data)) // 通过仿函数来修改比较规则
			{
				parent = cur;
				cur = cur->right;
			}
			else if (KV(value) < KV(cur->data))
			{
				parent = cur;
				cur = cur->left;
			}
			else
			{
				return { Iterator(cur,root),false }; // 插入失败  
			}
		}

		// 创建新节点并插入  
		cur = new Node(value); // 创建新节点  
		cur->col = red;      // 新节点初始为红色  

		Node* newnode = cur;//在调整前记录新节点位置

		// 将新节点链接到父节点  
		if (KV(cur->data) > KV(parent->data)) // 新节点键大于父节点键,插入右边  
		{
			parent->right = cur;   // 将新节点链接到父节点的右子节点  
			cur->_parent = parent; // 设置新节点的父节点  
		}
		else // 新节点键小于父节点键,插入左边  
		{
			parent->left = cur;    // 将新节点链接到父节点的左子节点  
			cur->_parent = parent; // 设置新节点的父节点  
		}

		// 根据红黑树性质,调整红黑树  
		while (parent && parent->col == red) // 检查父节点是否为红色  
		{
			Node* grandfather = parent->_parent; // 获取祖父节点  

			if (parent == grandfather->left) // 如果父节点是祖父节点的左子节点  
			{
				Node* uncle = grandfather->right; // 叔叔节点  
				if (uncle && uncle->col == red) // 再次检查叔叔节点颜色  
				{
					// 情况1:叔叔节点也为红色  
					uncle->col = parent->col = black; // 将父节点和叔叔节点设置为黑色  
					grandfather->col = red;           // 将祖父节点设置为红色  
					cur = grandfather;                // 继续向上调整  
					parent = cur->_parent;            // 更新父节点  
				}
				else // 叔叔节点为黑色  
				{
					if (cur == parent->left) // 情况2:新插入节点是父节点的左子节点  
					{
						RotateR(grandfather); // 旋转祖父节点向右  
						parent->col = black;  // 设置父节点为黑色  
						grandfather->col = red; // 设置祖父节点为红色  
					}
					else // 情况3:新插入节点是父节点的右子节点  
					{
						RotateL(parent);          // 先左旋转父节点  
						RotateR(grandfather);     // 再右旋转祖父节点  
						cur->col = black;         // 设置新节点为黑色  
						grandfather->col = red;   // 设置祖父节点为红色  
					}
					break; // 调整完成,退出循环  
				}
			}
			else // 如果父节点是祖父节点的右子节点  
			{
				Node* uncle = grandfather->left; // 叔叔节点  
				if (uncle && uncle->col == red) // 再次检查叔叔节点颜色  
				{
					// 情况1:叔叔节点也为红色  
					uncle->col = parent->col = black; // 将父节点和叔叔节点设置为黑色  
					grandfather->col = red;           // 将祖父节点设置为红色  
					cur = grandfather;                // 继续向上调整  
					parent = cur->_parent;            // 更新父节点  
				}
				else // 叔叔节点为黑色  
				{
					if (cur == parent->right) // 情况2:新插入节点是父节点的右子节点  
					{
						RotateL(grandfather); // 旋转祖父节点向左  
						parent->col = black;  // 设置父节点为黑色  
						grandfather->col = cur->col = red; // 设置祖父节点为红色,保持新节点为红色  
					}
					else // 情况3:新插入节点是父节点的左子节点  
					{
						RotateR(parent);          // 先右旋转父节点  
						RotateL(grandfather);     // 再左旋转祖父节点  
						grandfather->col = red;   // 设置祖父节点为红色  
						cur->col = black;         // 设置新节点为黑色  
					}
					break; // 调整完成,退出循环  
				}
			}
		}

		// 确保根节点始终为黑色  
		root->col = black;
		return { Iterator(newnode,root),false };  //返回新节点位置,而不是cur的位置 
	}

	// 递归检查红黑树的性质  
	bool _isRBtree(const Node* root, int num, int size)
	{
		// 如果当前节点是空,检查黑色节点数量  
		if (root == nullptr)
		{
			// 如果当前黑色节点数量不等于树中应有的数量,则返回失败  
			if (num != size)
			{
				cout << "黑色节点数量不对" << endl; // 输出错误信息  
				return false; // 返回 false,表示不符合红黑树性质  
			}
			return true; // 空节点,符合红黑树性质  
		}

		// 如果当前节点是黑色,增加黑色节点计数  
		if (root->col == black)
		{
			size++; // 增加黑色节点的计数  
		}

		// 检查是否有连续的红色节点  
		if (root->col == red && root->_parent != nullptr && root->_parent->col == red)
		{
			cout << "有连续的红色节点" << endl; // 输出错误信息  
			return false; // 返回 false,表示不符合红黑树性质  
		}

		// 递归检查左子树和右子树  
		return _isRBtree(root->left, num, size) && _isRBtree(root->right, num, size);
	}

	// 检查整个红黑树的性质  
	bool isRBtree()
	{
		// 根节点必须是黑色  
		if (root->col == red)
			return false; // 根节点为红色,返回 false  

		// 树为空,满足红黑树性质  
		if (root == nullptr)
			return true;

		// 从根节点开始  
		Node* cur = root;
		int num = 0; // 用于统计黑色节点数量  

		// 统计从根节点到最左叶子的路径中的黑色节点数量  
		while (cur)
		{
			if (cur->col == black) // 如果当前节点是黑色  
				num++; // 增加黑色节点计数  
			cur = cur->left; // 移动到左子节点  
		}

		int size = 0; // 用于记录路径上统计到的黑色节点数量  
		return _isRBtree(root, num, size); // 递归检查树的性质  
	}
private:
	void RotateL(Node*& parent)//左单旋
	{
		KeyOfValue KV;
		Node* subR = parent->right;
		Node* subRL = subR->left;
		Node* pparentnode = parent->_parent;

		parent->right = subRL;
		subR->left = parent;
		parent->_parent = subR;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		if (pparentnode == nullptr)
		{
			root = subR;
			root->_parent = nullptr;
		}
		else
		{
			if (KV(pparentnode->data) > KV(subR->data))
			{
				pparentnode->left = subR;
				subR->_parent = pparentnode;
			}
			else
			{
				pparentnode->right = subR;
				subR->_parent = pparentnode;
			}

		}
	}

	void RotateR(Node*& parent)//右单旋
	{
		KeyOfValue KV;
		Node* subL = parent->left;
		Node* subLR = subL->right;
		Node* pparentnode = parent->_parent;
		parent->left = subLR;
		subL->right = parent;
		parent->_parent = subL;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		if (pparentnode == nullptr)
		{
			root = subL;
			root->_parent = nullptr;
		}
		else
		{
			if (KV(pparentnode->data) > KV(subL->data))
			{
				pparentnode->left = subL;
				subL->_parent = pparentnode;
			}
			else
			{
				pparentnode->right = subL;
				subL->_parent = pparentnode;
			}
		}
	}


	// 拷贝构造函数
	RBtree(const RBtree& t)
	{
		root = copy(t.root, nullptr);
	}

	// 拷贝函数,递归深拷贝所有节点
	Node* copy(const Node* cur, Node* parent)
	{
		if (cur == nullptr)
			return nullptr;

		// 创建新节点
		Node* newnode = new Node(cur->data);
		newnode->col = cur->col;     // 拷贝节点颜色
		newnode->_parent = parent;   // 设置新节点的父节点

		// 递归拷贝左右子树
		newnode->left = copy(cur->left, newnode);
		newnode->right = copy(cur->right, newnode);

		return newnode;
	}

	// 递归删除所有节点
	void deletenode(Node* cur)
	{
		if (cur == nullptr)
			return;

		deletenode(cur->left);
		deletenode(cur->right);
		delete cur;
	}

	// 析构函数
	~RBtree()
	{
		deletenode(root);
	}
	Node* root = nullptr;
};

myset.h代码

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

enum colour
{
	black,
	red
};
template<class V>
struct RBtreeNode
{
	RBtreeNode<V>* left;
	RBtreeNode<V>* right;
	RBtreeNode<V>* _parent;
	V data;
	colour col = red;
	RBtreeNode(const V& key) :left(nullptr), right(nullptr), _parent(nullptr), data(key) {};
};


template<class V, class Ref, class Btf>
class RBiterator
{
public:
	typedef RBtreeNode<V> Node;
	typedef RBiterator<V, Ref, Btf> self;
	RBiterator(Node* tamp, Node* root) :cur(tamp), _root(root) {};//构造函数

	Ref operator*()
	{
		return cur->data;
	}

	Btf operator->()
	{
		return &(cur->data);
	}




	// 重载前置++操作符,向后迭代
	self& operator++()
	{
		// 如果右子树存在,找到右子树的最左节点
		if (cur->right)
		{
			Node* rightnode = cur->right;
			while (rightnode->left)
			{
				rightnode = rightnode->left;
			}
			cur = rightnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}


	self& operator--()
	{
		if (cur == nullptr)
		{
			Node* tamp = _root;
			while (tamp->right)
			{
				tamp = tamp->right;
			}
			cur = tamp;
		}
		// 如果左子树存在,找到左子树的最右节点
		else if (cur->left)
		{
			Node* leftnode = cur->left;
			while (leftnode->right)
			{
				leftnode = leftnode->right;
			}
			cur = leftnode;
		}
		else
		{
			// 否则,向上回溯到第一个父节点
			Node* parent = cur->_parent;
			while (parent && cur == parent->left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			cur = parent;
		}
		return *this;
	}

	self operator++(int)
	{
		self temp = *this; // 创建当前对象的副本
		++(*this); // 调用前置++
		return temp; // 返回副本
	}

	self operator--(int)
	{
		self temp = *this; // 创建当前对象的副本
		--(*this); // 调用前置--
		return temp; // 返回副本
	}



	bool operator !=(const self& tamp)
	{
		return cur != tamp.cur;
	}

	bool operator ==(const self& tamp)
	{
		return cur == tamp.cur;
	}
private:
	Node* cur;
	Node* _root;
};



template<class K, class V, class KeyOfValue>
class RBtree
{
public:
	typedef RBtreeNode<V> Node;
	typedef  RBiterator<V, V&, V*> Iterator;
	typedef  RBiterator<V, const V&, const V*> constiterator;

	Iterator begin()
	{
		Node* cur = root;
		while (cur && cur->left)
		{
			cur = cur->left;
		}
		return Iterator(cur, root);
	}
	Iterator end()
	{
		return Iterator(nullptr, root);
	}
	Iterator begin() const
	{
		Node* cur = root;
		while (cur && cur->left)
		{
			cur = cur->left;
		}
		return constiterator(cur, root);
	}
	Iterator end() const
	{
		return constiterator(nullptr, root);
	}


	pair<Iterator, bool> insert(const V& value)//修改插入类型
	{
		// 如果树为空,则插入新节点作为根节点  
		if (root == nullptr)
		{
			root = new Node(value); // 创建新节点  
			root->col = black;    // 根节点总是黑色  
			root->_parent = nullptr; // 根节点没有父节点  
		}

		// 初始化当前节点和父节点  
		Node* cur = root;
		Node* parent = cur->_parent;
		KeyOfValue KV;//实例化KeyOfValue
		// 查找插入位置  
		while (cur)
		{
			if (KV(value) > KV(cur->data)) // 通过仿函数来修改比较规则
			{
				parent = cur;
				cur = cur->right;
			}
			else if (KV(value) < KV(cur->data))
			{
				parent = cur;
				cur = cur->left;
			}
			else
			{
				return { Iterator(cur,root),false }; // 插入失败  
			}
		}

		// 创建新节点并插入  
		cur = new Node(value); // 创建新节点  
		cur->col = red;      // 新节点初始为红色  

		Node* newnode = cur;//在调整前记录新节点位置

		// 将新节点链接到父节点  
		if (KV(cur->data) > KV(parent->data)) // 新节点键大于父节点键,插入右边  
		{
			parent->right = cur;   // 将新节点链接到父节点的右子节点  
			cur->_parent = parent; // 设置新节点的父节点  
		}
		else // 新节点键小于父节点键,插入左边  
		{
			parent->left = cur;    // 将新节点链接到父节点的左子节点  
			cur->_parent = parent; // 设置新节点的父节点  
		}

		// 根据红黑树性质,调整红黑树  
		while (parent && parent->col == red) // 检查父节点是否为红色  
		{
			Node* grandfather = parent->_parent; // 获取祖父节点  

			if (parent == grandfather->left) // 如果父节点是祖父节点的左子节点  
			{
				Node* uncle = grandfather->right; // 叔叔节点  
				if (uncle && uncle->col == red) // 再次检查叔叔节点颜色  
				{
					// 情况1:叔叔节点也为红色  
					uncle->col = parent->col = black; // 将父节点和叔叔节点设置为黑色  
					grandfather->col = red;           // 将祖父节点设置为红色  
					cur = grandfather;                // 继续向上调整  
					parent = cur->_parent;            // 更新父节点  
				}
				else // 叔叔节点为黑色  
				{
					if (cur == parent->left) // 情况2:新插入节点是父节点的左子节点  
					{
						RotateR(grandfather); // 旋转祖父节点向右  
						parent->col = black;  // 设置父节点为黑色  
						grandfather->col = red; // 设置祖父节点为红色  
					}
					else // 情况3:新插入节点是父节点的右子节点  
					{
						RotateL(parent);          // 先左旋转父节点  
						RotateR(grandfather);     // 再右旋转祖父节点  
						cur->col = black;         // 设置新节点为黑色  
						grandfather->col = red;   // 设置祖父节点为红色  
					}
					break; // 调整完成,退出循环  
				}
			}
			else // 如果父节点是祖父节点的右子节点  
			{
				Node* uncle = grandfather->left; // 叔叔节点  
				if (uncle && uncle->col == red) // 再次检查叔叔节点颜色  
				{
					// 情况1:叔叔节点也为红色  
					uncle->col = parent->col = black; // 将父节点和叔叔节点设置为黑色  
					grandfather->col = red;           // 将祖父节点设置为红色  
					cur = grandfather;                // 继续向上调整  
					parent = cur->_parent;            // 更新父节点  
				}
				else // 叔叔节点为黑色  
				{
					if (cur == parent->right) // 情况2:新插入节点是父节点的右子节点  
					{
						RotateL(grandfather); // 旋转祖父节点向左  
						parent->col = black;  // 设置父节点为黑色  
						grandfather->col = cur->col = red; // 设置祖父节点为红色,保持新节点为红色  
					}
					else // 情况3:新插入节点是父节点的左子节点  
					{
						RotateR(parent);          // 先右旋转父节点  
						RotateL(grandfather);     // 再左旋转祖父节点  
						grandfather->col = red;   // 设置祖父节点为红色  
						cur->col = black;         // 设置新节点为黑色  
					}
					break; // 调整完成,退出循环  
				}
			}
		}

		// 确保根节点始终为黑色  
		root->col = black;
		return { Iterator(newnode,root),false };  //返回新节点位置,而不是cur的位置 
	}

	// 递归检查红黑树的性质  
	bool _isRBtree(const Node* root, int num, int size)
	{
		// 如果当前节点是空,检查黑色节点数量  
		if (root == nullptr)
		{
			// 如果当前黑色节点数量不等于树中应有的数量,则返回失败  
			if (num != size)
			{
				cout << "黑色节点数量不对" << endl; // 输出错误信息  
				return false; // 返回 false,表示不符合红黑树性质  
			}
			return true; // 空节点,符合红黑树性质  
		}

		// 如果当前节点是黑色,增加黑色节点计数  
		if (root->col == black)
		{
			size++; // 增加黑色节点的计数  
		}

		// 检查是否有连续的红色节点  
		if (root->col == red && root->_parent != nullptr && root->_parent->col == red)
		{
			cout << "有连续的红色节点" << endl; // 输出错误信息  
			return false; // 返回 false,表示不符合红黑树性质  
		}

		// 递归检查左子树和右子树  
		return _isRBtree(root->left, num, size) && _isRBtree(root->right, num, size);
	}

	// 检查整个红黑树的性质  
	bool isRBtree()
	{
		// 根节点必须是黑色  
		if (root->col == red)
			return false; // 根节点为红色,返回 false  

		// 树为空,满足红黑树性质  
		if (root == nullptr)
			return true;

		// 从根节点开始  
		Node* cur = root;
		int num = 0; // 用于统计黑色节点数量  

		// 统计从根节点到最左叶子的路径中的黑色节点数量  
		while (cur)
		{
			if (cur->col == black) // 如果当前节点是黑色  
				num++; // 增加黑色节点计数  
			cur = cur->left; // 移动到左子节点  
		}

		int size = 0; // 用于记录路径上统计到的黑色节点数量  
		return _isRBtree(root, num, size); // 递归检查树的性质  
	}

	RBtree() = default;
	// 拷贝构造函数
	RBtree(const RBtree& t)
	{
		root = copy(t.root, nullptr);
	}
	// 析构函数
	~RBtree()
	{
		deletenode(root);
	}
private:
	void RotateL(Node*& parent)//左单旋
	{
		KeyOfValue KV;
		Node* subR = parent->right;
		Node* subRL = subR->left;
		Node* pparentnode = parent->_parent;

		parent->right = subRL;
		subR->left = parent;
		parent->_parent = subR;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		if (pparentnode == nullptr)
		{
			root = subR;
			root->_parent = nullptr;
		}
		else
		{
			if (KV(pparentnode->data) > KV(subR->data))
			{
				pparentnode->left = subR;
				subR->_parent = pparentnode;
			}
			else
			{
				pparentnode->right = subR;
				subR->_parent = pparentnode;
			}

		}
	}

	void RotateR(Node*& parent)//右单旋
	{
		KeyOfValue KV;
		Node* subL = parent->left;
		Node* subLR = subL->right;
		Node* pparentnode = parent->_parent;
		parent->left = subLR;
		subL->right = parent;
		parent->_parent = subL;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		if (pparentnode == nullptr)
		{
			root = subL;
			root->_parent = nullptr;
		}
		else
		{
			if (KV(pparentnode->data) > KV(subL->data))
			{
				pparentnode->left = subL;
				subL->_parent = pparentnode;
			}
			else
			{
				pparentnode->right = subL;
				subL->_parent = pparentnode;
			}
		}
	}

	

	// 拷贝函数,递归深拷贝所有节点
	Node* copy(const Node* cur, Node* parent)
	{
		if (cur == nullptr)
			return nullptr;

		// 创建新节点
		Node* newnode = new Node(cur->data);
		newnode->col = cur->col;     // 拷贝节点颜色
		newnode->_parent = parent;   // 设置新节点的父节点

		// 递归拷贝左右子树
		newnode->left = copy(cur->left, newnode);
		newnode->right = copy(cur->right, newnode);

		return newnode;
	}

	// 递归删除所有节点
	void deletenode(Node* cur)
	{
		if (cur == nullptr)
			return;

		deletenode(cur->left);
		deletenode(cur->right);
		delete cur;
	}

	Node* root = nullptr;
};

myset.h文件代码

#include "RBtree.h"  // 确保这个文件存在并且被正确实现  

namespace myworld {
    template<class K>
    class set {
    public:
        struct KeyOfValue
        {
        public:
            K operator()(const K& num)
            {
                return num;
            }
        };
        typedef typename RBtree<K, const K, KeyOfValue> ::Iterator iterator;
        typedef typename RBtree<K, const K, KeyOfValue> ::constiterator const_iterator;

        iterator begin()
        {
            return rbTree.begin();
        }

        iterator end()
        {
            return rbTree.end();
        }
        iterator begin() const
        {
            return rbTree.begin();
        }

        iterator end() const
        {
            return rbTree.end();
        }


        pair<iterator, bool> insert(const K& data)
        {
            pair<iterator, bool> ret = rbTree.insert(data);
            return ret;
        }


    private:
        RBtree<K,const K, KeyOfValue> rbTree;
    };
}

mymap.h的代码

#pragma once
#include"RBtree.h"

namespace myworld
{
	template<class K, class V>
	class map
	{
	public:
        struct KeyOfValue
        {
        public:
            K operator()(const pair<K, V>& num)
            {
                return num.first;//仿函数改变比较的对象
            }
        };

        typedef typename RBtree<K, pair<const K, V>, KeyOfValue> ::Iterator iterator;
        typedef typename RBtree<K, pair<const K, V>, KeyOfValue> ::constiterator const_iterator;

        iterator begin()
        {
            return rbTree.begin();
        }

        iterator end()
        {
            return rbTree.end();
        }
        iterator begin() const
        {
            return rbTree.begin();
        }

        iterator end() const
        {
            return rbTree.end();
        }

        pair<iterator, bool> insert(const pair<K,V>& data)
        {
            return rbTree.insert(data);
        }

        V& operator[](const K&key)
        {
            pair<iterator, bool> ret=insert(make_pair(key,V()));
            return ret.first->second;
        }

	private:
        RBtree<K, pair<const K, V>, KeyOfValue> rbTree;//KeyOfValue作为类型模版传过去
	};
}

回答: ES6中的SetMap是两种新增的集合类型。Set是一种无重复值的集合,可以通过new Set()来创建。它具有add方法用于向集合中添加元素,has方法用于判断集合中是否存在某个元素,clear方法用于清空集合。Set也可以用于数组去重,通过new Set(\[...\])的方式将数组转换为Set,利用Set的特性去除重复值。\[1\] Map是一种键值对的集合,可以通过new Map()来创建。它具有set方法用于向集合中添加键值对,get方法用于获取指定键名对应的值,has方法用于判断集合中是否存在某个键名,clear方法用于清空集合。Map的键名可以是任意数据类型,包括引用值,但需要注意的是,引用值作为键名时,需要使用相同的引用地址才能获取到对应的值。\[2\] SetMap都可以使用for...of循环或forEach方法进行遍历。在Set中,forEach的第二个参数是集合的元素,因为Set中不存在下标。而在Map中,forEach的第一个参数是键值对的值,第二个参数是键值对的键名。\[3\] 总结来说,SetMap是ES6中新增的集合类型,Set用于存储无重复值的集合,Map用于存储键值对的集合。它们提供了一些方法来操作集合,如添加、获取、判断是否存在等。在使用时需要注意它们的特性和方法的使用方式。 #### 引用[.reference_title] - *1* *2* [ES6中的MapSet详解](https://blog.csdn.net/m0_45093055/article/details/126430467)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [ES6中的setmap](https://blog.csdn.net/weixin_44247866/article/details/127561391)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值