set 类型

map 容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set 容器只是单纯的键的集合。例如,某公司可能定义了一个名为 bad_checks的 set 容器,用于记录曾经给本公司发空头支票的客户。当只想知道一个值是否存在时,使用 set 容器是最适合的。例如,在接收一张支票前,该公司可能想查询 bad_checks 对象,看看该客户的名字是否存在。

set 不支持下标操作符,而且没有定义 mapped_type 类型。在 set 容器中,value_type 不是 pair 类型,而是与 key_type 相同的类型。它们指的都是 set 中存储的元素类型。这一差别也体现了 set 存储的元素仅仅是键,而没有所关联的值。与 map 一样,set 容器存储的键也必须唯一,而且

不能修改。

set 容器的定义和使用

为了使用 set 容器,必须包含 set 头文件。

与 map 容器一样,set 容器的每个键都只能对应一个元素。以一段范围的元素初始化 set 对象,或在 set 对象中插入一组元素时,对于每个键,事实上都只添加了一个元素:

// define a vector with 20 elements, holding two copies of each number from 0 to 9
vector<int> ivec;
for (vector<int>::size_type i = 0; i != 10; ++i) {
ivec.push_back(i);
ivec.push_back(i); // duplicate copies of each number
}
// iset holds unique elements from ivec
set<int> iset(ivec.begin(), ivec.end());
cout << ivec.size() << endl; // prints 20
cout << iset.size() << endl; // prints 10

首先创建了一个名为 ivec 的 int 型 vector 容器,存储 20 个元素:0-9(包括 9)中每个整数都出现了两次。然后用 ivec 中所有的元素初始化一个int 型的 set 容器。则这个 set 容器仅有 10 个元素:ivec 中不相同的各个元素。

在 set 中添加元素

可使用 insert 操作在 set 中添加元素:

set<string> set1; // empty set
set1.insert("the"); // set1 now has one element
set1.insert("and"); // set1 now has two elements

另一种用法是,调用 insert 函数时,提供一对迭代器实参,插入其标记范围内所有的元素。该版本的 insert 函数类似于形参为一对迭代器的构造函数——对于一个键,仅插入一个元素:

set<int> iset2; // empty set
iset2.insert(ivec.begin(), ivec.end()); // iset2 has 10 elements

与 map 容器的操作一样,带有一个键参数的 insert 版本返回 pair 类型对象, 包含一个迭代器和一个 bool 值, 迭代器指向拥有该键的元素, 而 bool 值表明是否添加了元素。使用迭代器对的 insert 版本返回 void 类型。

从 set 中获取元素

set 容器不提供下标操作符。为了通过键从 set 中获取元素,可使用 find运算。如果只需简单地判断某个元素是否存在,同样可以使用 count 运算,返回 set 中该键对应的元素个数。当然,对于 set 容器,count 的返回值只能是1(该元素存在)或 0(该元素不存在):

iset.find(1) // returns iterator that refers to the element with key == 1
iset.find(11) // returns iterator == iset.end()
iset.count(1) // returns 1
iset.count(11) // returns 0

正如不能修改 map 中元素的键部分一样,set 中的键也为 const。在获得指向 set 中某元素的迭代器后,只能对其做读操作,而不能做写操作:

// set_it refers to the element with key == 1
set<int>::iterator set_it = iset.find(1);
*set_it = 11; // error: keys in a set are read-only
cout << *set_it << endl; // ok: can read the key

使用实例

创建“单词排除”集:

之前的程序使用从 map 对象 word_count 中删除一个指定的单词。可将这个操作扩展为删除指定文件中所有的单词(即该文件记录的是排除集)。也即,我们的单词统计程序只对那些不在排除集中的单词进行统计。使用 set 和map 容器,可以简单而直接地实现该功能:

void restricted_wc(ifstream &remove_file,
map<string, int> &word_count)
{
set<string> excluded; // set to hold words we'll ignore
string remove_word;
while (remove_file >> remove_word)
excluded.insert(remove_word);
// read input and keep a count for words that aren't in the
exclusion set
string word;
while (cin >> word)
// increment counter only if the word is not in excluded
if (!excluded.count(word))
++word_count[word];
}

该函数首先读取传递进来的文件,该文件列出了所有被排除的单词。读入这些单词并存储在一个名为 excluded 的 set 容器中。第一个 while 循环完成后,该 set 对象包含了输入文件中的所有单词。

接下来的程序类似原来的单词统计程序。关键的区别在于:在统计每个单词之前,先检查该单词是否出现在排除集中。第二个 while 循环里的 if 语句实现了该功能:

// increment counter only if the word is not in excluded
if (!excluded.count(word))

如果该单词出现在排除集 excluded 中,则调用 count 将返回 1,否则返回 0。对 count 的返回值做“非”运算,则当该 word 不在 excluded 中时,条件测试成功,此时修改该单词在 map 中对应的值。

与单词统计程序原来的版本一样,需要使用下标操作符的性质:如果某键尚未在map 容器中出现,则将该元素插入容器。所以语句;

++word_count[word];

的效果是:如果 word 还没出现过,则将它插入到 word_count 中,并在插入元素后,将它关联的值初始化为 0。然后不管是否插入了新元素,相应元素的值都加 1。

posted @ 2018-05-07 23:11  刘-皇叔  阅读(2343)  评论(0编辑  收藏  举报