c++关联容器

AS3.0的动态类,如 Object MovieClip Dictionary 类 也是 是个关联容器 

摘自C++ primer

关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。

 

关联容器(Associative containers)支持通过键来高效地查找和读取元素。两个基本的关联容器类型是 mapsetmap 的元素以键-值(key-value)对的形式组织:键用作元素在 map 中的索引,而值则表示所存储和读取的数据。set 仅包含一个键,并有效地支持关于某个键是否存在的查询。

 

表 10.1. 关联容器类型

 

map

Associative array; elements stored and retrieved by key

关联数组:元素通过键来存储和读取

set

Variable-sized collection with fast retri by key

大小可变的集合,支持通过键实现的快速读取

multimap

map in which a key can appear multiple times

支持同一个键多次出现的 map 类型

multiset

set in which a key can appear multiple times

支持同一个键多次出现的 set 类型

 

 一般来说,如果希望有效地存储不同值的集合,那么使用 set 容器比较合适,而 map 容器则更适用于需要存储(乃至修改)每个键所关联的值的情况。在做某种文本处理时,可使用 set 保存要忽略的单词。而字典则是 map 的一种很好的应用:单词本身是键,而它的解释说明则是值。

 

 set 和 map 类型的对象所包含的元素都具有不同的键,不允许为同一个键添加第二个元素。如果一个键必须对应多个实例,则需使用 multimapmulti set,这两种类型允许多个元素拥有相同的键。

 

 

在开始介绍关联容器之前,必须先了解一种与之相关的简单的标准库类型——pair表 10.2),该类型在 utility 头文件中定义。 

表 10.2 pairs 类型提供的操作

 

pair<T1, T2> p1;

创建一个空的 pair 对象,它的两个元素分别是 T1T2 类型,采用值初始化

pair<T1, T2> p1(v1, v2);

 
 

创建一个 pair 对象,它的两个元素分别是 T1T2 ,其中 first 成员初始化为 v1,而 second 成员初始化为 v2

make_pair(v1, v2)

v1v2 值创建一个新 pair 对象,其元素类型分别是 v1v2 的类型

p1 < p2

两个 pair 对象之间的小于运算,其定义遵循字典次序:如果 p1.first < p2.first 或者 !(p2.first < p1.first) && p1.second < p2.second,则返回 true

p1 == p2

如果两个 pair 对象的 firstsecond 成员依次相等,则这两个对象相等。该运算使用其元素的 == 操作符

p.first

返回 p 中名为 first 的(公有)数据成员

p.second

返回 p 的名为 second 的(公有)数据成员

 

 

 

 pair 的创建和初始化

如果在创建 pair 对象时不提供初始化式,则调用默认构造函数对其成员采用值初始化。

     pair<string, string> anon;       // holds two strings
     pair<string, int> word_count;    // holds a string and an int
     pair<string, vector<int> > line; // holds string and vector<int>

 

     pair<string, string> author("James", "Joyce");

pair 类型的使用相当繁琐,因此,如果需要定义多个相同的 pair 类型对象,可考虑利用 typedef 简化其声明:

     typedef pair<string, string> Author;
     Author proust("Marcel", "Proust");
     Author joyce("James", "Joyce");

与其他标准库类型不同,对于 pair 类,可以直接访问其数据成员:其成员都是仅有的,分别命名为 firstsecond

     string firstBook;
     // access and test the data members of the pair
     if (author.first == "James" && author.second == "Joyce")
         firstBook = "Stephen Hero";

除了构造函数,标准库还定义了一个 make_pair 函数,由传递给它的两个实参生成一个新的 pair 对象。可如下使用该函数创建新的 pair 对象,并赋给已存在的 pair 对象:

     pair<string, string> next_auth;
     string first, last;
     while (cin >> first >> last) {
         // generate a pair from first and last
         next_auth = make_pair(first, last);
         // process next_auth...
     }

make_pair 函数生成一个新的 pair 对象,此操作等价于下面更复杂的操作:

     // use pair constructor to make first and last into a pair
     next_auth = pair<string, string>(first, last);

由于 pair 的数据成员是公有的,因而可如下直接地读取输入:

     pair<string, string> next_auth;
     // read directly into the members of next_auth
     while (cin >> next_auth.first >> next_auth.second) {
         // process next_auth...
     }

 

 

“关联容器元素根据键的次序排列”这一事实就是一个重要的结论:在迭代遍历关联容器时,我们可确保按键的顺序的访问元素,而与元素在容器中的存放位置完全无关。

 

 

map 是键-值对的集合。map 类型通常可理解为关联数组(associative array):可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。 

 

map 对象的定义

表 10.3. map 的构造函数

 

map<k, v> m;

创建一个名为 m 的空 map 对象,其键和值的类型分别为 kv

map<k, v> m(m2);

创建 m2 的副本 mmm2 必须有相同的键类型和值类型

map<k, v> m(b, e);

创建 map 类型的对象 m,存储迭代器 be 标记的范围内所有元素的副本。元素的类型必须能转换为 pair<const k, v>

 

 

 

 

在实际应用中,键类型必须定义 < 操作符,而且该操作符应能“正确地工作”,这一点很重要。

在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。 所用的比较函数必须在键类型上定义严格弱排序(strict weak ordering)。所谓的严格弱排序可理解为键类型数据上的“小于”关系。当用于一个键与自身的比较时,肯定会导致 false 结果。如果它们相互之间都不存在“小于”关系,则容器将之视为相同的键。用做 map 对象的键时,可使用任意一个键值来访问相应的元素。

 

对于键类型,唯一的约束就是必须支持 < 操作符,至于是否支持其他的关系或相等运算,则不作要求。

 

map 对象的元素是键-值对,其 value_type 是存储元素的键以及值的 pair 类型,而且键为 const

在学习 map 的接口时,需谨记 value_typepair 类型,它的值成员可以修改,但键成员不能修改。

表 10.4. map 类定义的类型

 

map<K, V>::key_type

The type of the keys used to index the map.

map 容器中,用做索引的键的类型

map<K, V>::mapped_type

map 容器中,键所关联的值的类型

map<K, V>::value_type

一个 pair 类型,它的 first 元素具有 const map<K, V>::key_type 类型,而 second 元素则为 map<K, V>::mapped_type 类型

 

 

 

     // count number of times each word occurs in the input
     map<string, int> word_count; // empty map from string to int 

 

    // get an iterator to an element in word_count
     map<string, int>::iterator map_it = word_count.begin();

     // *map_it is a reference to a pair<const string, int> object
     cout << map_it->first;                  // prints the key for this element
     cout << " " << map_it->second;          // prints the value of the element
     map_it->first = "new key";              // error: key is const
     ++map_it->second;     // ok: we can change value through an iterator
 

map 类额外定义了两种类型:key_typemapped_type,以获得键或值的类型。

 

 

map 添加元素 

可使用 insert 成员实现;或者,先用下标操作符获取元素,然后给获取的元素赋值。在这两种情况下,一个给定的键只能对应于一个元素这一事实影响了这些操作的行为。 

如下编写程序时:

     map <string, int> word_count; // empty map

     // insert default initialzed element with key Anna; then assign 1 to its value
     word_count["Anna"] = 1;

使用下标访问 map 与使用下标访问数组或 vector 的行为截然不同:用下标访问不存在的元素将导致在 map 容器中添加一个新元素,它的键即为该下标值。

下标操作符返回值的使用

通常来说,下标操作符返回左值。它返回的左值是特定键所关联的值。可如下读或写元素:

     cout << word_count["Anna"]; // fetch element indexed by Anna; prints 1
     ++word_count["Anna"];       // fetch the element and add one to it
     cout << word_count["Anna"]; // fetch the element and print it; prints 2

有别于 vectorstring 类型,map 下标操作符返回的类型与对 map 迭代器进行解引用获得的类型不相同。

显然,map 迭代器返回 value_type 类型的值——包含 const key_typemapped_type 类型成员的 pair 对象;下标操作符则返回一个 mapped_type 类型的值。

 

对于 map 容器,如果下标所表示的键在容器中不存在,则添加新元素,这一特性可使程序惊人地简练:

这段程序创建一个 map 对象,用来记录每个单词出现的次数。

     // count number of times each word occurs in the input
     map<string, int> word_count; // empty map from string to int
     string word;
     while (cin >> word)
       ++word_count[word];

 

表 10.5. map 容器提供的 insert 操作

 

m.insert(e)

e is a value of the value_type for m. If the key(e.first) is not in m, inserts a new element with value e.second. If the key is in m, then m is unchanged. Returns a pair containing a map iterator referring to the element with key e.first and a bool indicating whether the element was inserted or not.

e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则插入一个值为 e.second 的新元素;如果该键在 m 中已存在,则保持 m 不变。该函数返回一个 pair 类型对象,包含指向键为 e.first 的元素的 map 迭代器,以及一个 bool 类型的对象,表示是否插入了该元素

m.insert(beg, end)

begend 是标记元素范围的迭代器,其中的元素必须为 m.value_type 类型的键-值对。对于该范围内的所有元素,如果它的键在 m 中不存在,则将该键及其关联的值插入到 m。返回 void 类型

m.insert(iter, e)

e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则创建新元素,并以迭代器 iter 为起点搜索新元素存储的位置。返回一个迭代器,指向 m 中具有给定键的元素

 

 

 

以 insert 代替下表运算

插入元素的另一个方法是:直接使用 insert 成员,其语法更紧凑:

     // if Anna not already in word_count, inserts new element with value 1
     word_count.insert(map<string, int>::value_type("Anna", 1));

传递给 insert 的实参相当笨拙。可用两种方法简化:使用 make_pair:

     word_count.insert(make_pair("Anna", 1));

或使用 typedef

     typedef map<string,int>::value_type valType;
     word_count.insert(valType("Anna", 1));

带有一个键-值 pair 形参的 insert 版本将返回一个值:包含一个迭代器和一个 bool 值的 pair 对象,其中迭代器指向 map 中具有相应键的元素,而 bool 值则表示是否插入了该元素。如果该键已在容器中,则其关联的值保持不变,返回的 bool 值为 true。在这两种情况下,迭代器都将指向具有给定键的元素。下面是使用 insert 重写的单词统计程序:

     // count number of times each word occurs in the input
     map<string, int> word_count; // empty map from string to int
     string word;
     while (cin >> word) {
         // inserts element with key equal to word and value 1;
         // if word already in word_count, insert does nothing
        
pair<map<string, int>::iterator, bool> ret =
                   word_count.insert(make_pair(word, 1));
         if (!ret.second)          // word already in word_count
             ++ret.first->second;  // increment counter
     }
对于每个单词,都尝试 insert 它,并将它的值赋 1。if 语句检测 insert 函数返回值中的 bool 值。如果该值为 false,则表示没有做插入操作,按 word 索引的元素已在 word_count 中存在。此时,将该元素所关联的值加 1。

 

使用下标存在一个很危险的副作用:如果该键不在 map 容器中,那么下标操作会插入一个具有该键的新元素。

map 容器提供了两个操作:countfind,用于检查某个键是否存在而不会插入该键。 

表 10.6. 不修改 map 对象的查询操作

 

m.count(k)

返回 mk 的出现次数

m.find(k)

如果 m 容器中存在按 k 索引的元素,则返回指向该元素的迭代器。如果不存在,则返回超出末端迭代器

 

对于 map 对象,count 成员的返回值只能是 0 或 1。map 容器只允许一个键对应一个实例,所以 count 可有效地表明一个键是否存在。而对于 multimaps 容器,count 的返回值将有更多的用途。

 

如果返回值非 0,则可以使用下标操作符来获取该键所关联的值,而不必担心这样做会在 map 中插入新元素:

     int occurs = 0;
     if (word_count.count("foobar"))
         occurs = word_count["foobar"];

当然,在执行 count 后再使用下标操作符,实际上是对元素作了两次查找。如果希望当元素存在时就使用它,则应该用 find 操作。

 

find 操作返回指向元素的迭代器,如果元素不存在,则返回 end 迭代器:

     int occurs = 0;
     map<string,int>::iterator it = word_count.find("foobar");
     if (it != word_count.end())
         occurs = it->second;

如果希望当具有指定键的元素存在时,就获取该元素的引用,否则就不在容器中创建新元素,那么应该使用 find。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值