10.4. The set Type10.4. set 类型A map is a collection of a keyvalue pairs, such as an address and phone number keyed to an individual's name. In contrast, a set is simply a collection of keys. For example, a business might define a set named bad_checks, to hold the names of individuals who have issued bad checks to the company. A set is most useful when we simply want to know whether a value is present. Before accepting a check, for example, that business would query bad_checks to see whether the customer's name was present. map 容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set 容器只是单纯的键的集合。例如,某公司可能定义了一个名为 bad_checks 的 set 容器,用于记录曾经给本公司发空头支票的客户。当只想知道一个值是否存在时,使用 set 容器是最适合的。例如,在接收一张支票前,该公司可能想查询 bad_checks 对象,看看该客户的名字是否存在。 With two exceptions, set supports the same operations as map: 除了两种例外情况,set 容器支持大部分的 map 操作,包括下面几种:
The exceptions are that set does not provide a subscript operator and does not define mapped_type. In a set, the value_type is not a pair; instead it and key_type are the same type. They are each the type of the elements stored in the set. These differences reflect the fact that set holds only keys; there is no value associated with the key. As with map, the keys of a set must be unique and may not be changed. 两种例外包括:set 不支持下标操作符,而且没有定义 mapped_type 类型。在 set 容器中,value_type 不是 pair 类型,而是与 key_type 相同的类型。它们指的都是 set 中存储的元素类型。这一差别也体现了 set 存储的元素仅仅是键,而没有所关联的值。与 map 一样,set 容器存储的键也必须唯一,而且不能修改。 10.4.1. Defining and Using sets10.4.1. set 容器的定义和使用To use a set, we must include the set header. The operations on sets are essentially identical to those on maps. 为了使用 set 容器,必须包含 set 头文件。set 支持的操作基本上与 map 提供的相同。 As with map, there can be only one element with a given key in a set. When we initialize a set from a range of elements or insert a range of elements, only one element with a given key is actually added: 与 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 We first create a vector of ints named ivec that has 20 elements: two copies of each of the integers from 0 through 9 inclusive. We then use all the elements from ivec to initialize a set of ints. That set has only ten elements: one for each distinct element in ivec. 首先创建了一个名为 ivec 的 int 型 vector 容器,存储 20 个元素:0-9(包括 9)中每个整数都出现了两次。然后用 ivec 中所有的元素初始化一个 int 型的 set 容器。则这个 set 容器仅有 10 个元素:ivec 中不相同的各个元素。 Adding Elements to a set在 set 中添加元素We can add elements to a set by using the insert operation: 可使用 insert 操作在 set 中添加元素: set<string> set1; // empty set set1.insert("the"); // set1 now has one element set1.insert("and"); // set1 now has two elements Alternatively, we can insert a range of elements by providing a pair of iterators to insert. This version of insert works similarly to the constructor that takes an iterator paironly one element with a given key is inserted: 另一种用法是,调用 insert 函数时,提供一对迭代器实参,插入其标记范围内所有的元素。该版本的 insert 函数类似于形参为一对迭代器的构造函数——对于一个键,仅插入一个元素: set<int> iset2; // empty set iset2.insert(ivec.begin(), ivec.end()); // iset2 has 10 elements Like the map operations, the version of insert that takes a key returns a pair containing an iterator to the element with this key and a bool indicating whether the element was added. The one that takes an iterator pair returns void. 与 map 容器的操作一样,带有一个键参数的 insert 版本返回 pair 类型对象,包含一个迭代器和一个 bool 值,迭代器指向拥有该键的元素,而 bool 值表明是否添加了元素。使用迭代器对的 insert 版本返回 void 类型。 Fetching an Element from a set从 set 中获取元素There is no subscript operator on sets. To fetch an element from a set by its key, we use the find operation. If we just want to know whether the element is present, we could also use count, which returns the number of elements in the set with a given key. Of course, for set that value can be only one (if the element is present) or zero (if it is not): 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 Just as we cannot change the key part of a map element, the keys in a set are also const. If we have an iterator to an element of the set, all we can do is read it; we cannot write through it: 正如不能修改 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 10.4.2. Building a Word-Exclusion Set10.4.2. 创建“单词排除”集On page 369 we removed a given word from our word_count map. We might want to extend this approach to remove all the words in a specified file. That is, our word-count program should count only words that are not in a set of excluded words. Using set and map, this program is fairly straightforward: 第 10.3.7 节的程序从 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]; } This program is similar to the word-count program on page 363. The difference is that we do not bother to count the common words. 这个程序类似第 10.3.4 节的单词统计程序。其差别在于不需要费力地统计常见的单词。 The function starts by reading the file it was passed. That file contains the list of excluded words, which we store in the set named excluded. When the first while completes, that set contains an entry for each word in the input file. 该函数首先读取传递进来的文件,该文件列出了所有被排除的单词。读入这些单词并存储在一个名为 excluded 的 set 容器中。第一个 while 循环完成捍,该 set 对象包含了输入文件中的所有单词。 The next part of the program looks a lot like our original word-count program. The important difference is that before counting each word, we check whether the word is in the exclusion set. We do this check in the if inside the second while: 接下来的程序类似原来的单词统计程序。关键的区别在于:在统计每个单词之前,先检查该单词是否出现在排除集中。第二个 while 循环里的 if 语句实现了该功能: // increment counter only if the word is not in excluded if (!excluded.count(word)) The call to count returns one if word is in excluded and zero otherwise. We negate the return from count so that the test succeeds if word is not in excluded. If word is not in excluded, we update its value in the map. 如果该单词出现在排除集 excluded 中,则调用 count 将返回 1,否则返回 0。对 count 的返回值做“非”运算,则当该 word 不在 excluded 中时,条件测试成功,此时修改该单词在 map 中对应的值。 As in the previous version of our word count program, we rely on the fact that subscripting a map inserts an element if the key is not already in the map. Hence, the effect of 与单词统计程序原来的版本一样,需要使用下标操作符的性质:如果某键尚未在 map 容器中出现,则将该元素插入容器。所以语句 ++word_count[word]; is to insert word into word_count if it wasn't already there. If the element is inserted, its value is initially set to 0. Regardless of whether the element had to be created, the value is then incremented. 的效果是:如果 word 还没出现过,则将它插入到 word_count 中,并在插入元素后,将它关联的值初始化为 0。然后不管是否插入了新元素,相应元素的值都加 1。
![]() |