【问题标题】:Iterating over Unorderd_map using indexed for loop使用索引 for 循环遍历 Unorderd_map
【发布时间】:2015-11-23 13:42:49
【问题描述】:

我正在尝试使用for loop 访问存储在unorderd_map 中的值,但我一直在尝试使用循环的当前索引访问值。有什么建议或链接吗?谢谢。 [提示:我不想使用迭代器]。

我的示例代码:

#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;

int main()
{
    unordered_map<int,string>hash_table;
    //filling my hash table
    hash_table.insert(make_pair(1,"one"));
    hash_table.insert(make_pair(2,"two"));
    hash_table.insert(make_pair(3,"three"));
    hash_table.insert(make_pair(4,"four"));

   //now, i want to access values of my hash_table with for loop, `i` as index.
   //
  for (int i=0;i<hash_table.size();i++ )
  {
    cout<<"Value at index "<<i<<" is "<<hash_table[i].second;//I want to do something like this. I don't want to use iterator!
  }
  return 0;
}

【问题讨论】:

  • 您不能只对std::unordered_map(或std::map)使用普通数组索引,因为operator[] 函数需要一个键而不是索引。
  • 因此,您希望在 [1,4] 中进行迭代。
  • 也许你只想要一个std::vector&lt;std::pair&lt;int,string&gt; &gt;

标签: c++ hashtable unordered-map


【解决方案1】:

有两种方法可以从std::unordered_map 访问元素。

  1. 一个迭代器。
  2. 下标运算符,使用键。

我被困在尝试使用循环的当前索引访问值

如您所见,使用索引访问元素并未列在访问元素的可能方式中。

我相信您已经意识到,由于地图是无序,因此索引i 处的短语元素在排序方面毫无意义。 可以使用begin 迭代器和std::advance 访问ith 元素,但是...

提示:我不想使用迭代器]。

提示:你只是用完了选项。你想做的事情是不可能的。解决方案:开始希望使用适合实现目标的工具。

如果您想迭代std::unordered_map,那么您可以使用迭代器,因为这就是它们的用途。如果您不想使用迭代器,则不能迭代std::unordered_map。您可以使用基于范围的 for 循环隐藏迭代器的使用,但它们仍在幕后使用。

如果您想使用位置索引来迭代某些内容,那么您需要一个数组,例如 std::vector

【讨论】:

    【解决方案2】:

    首先,为什么要使用索引而不是迭代器?
    假设您有一个希望 UI 绘制的小部件列表。每个小部件都可以有自己的子小部件列表,存储在地图中。您的选择是:

    1. 让每个小部件自行绘制。不理想,因为小部件现在已与您正在使用的 UI 工具包耦合。
    2. 返回地图并在绘图代码中使用迭代器。不理想,因为现在绘图代码知道您的存储机制。

    可以避免这两种情况的 API 可能如下所示。

    const Widget* Widget::EnumerateChildren(size_t* io_index) const;

    您可以使用地图进行这项工作,但效率不高。您也无法保证调用之间映射的稳定性。所以这不是推荐,而是可能

    const Widget* Widget::EnumerateChildren(size_t* io_index) const
    {
        auto& it = m_children.begin();
        std::advance(it, *io_index);
        *io_index += 1;
        return it->second;
    }
    

    您不必使用 std::advance 并且可以使用 for 循环自己推进迭代器。效率不高或非常安全。

    对于我所描述的场景,更好的解决方案是将值复制到向量中。

    void Widget::GetChildren(std::vector<Widget*>* o_children) const;
    

    【讨论】:

    • 如果您想将地图中的元素添加到数组中,通过索引循环很方便。为什么?因为 Firecode 要我返回一个数组。
    【解决方案3】:

    如果没有迭代器,您将无法做到这一点。无序地图可以按任何顺序存储内容并随意移动它们。例如,“第 3 个元素”的概念没有任何意义。

    如果您有地图中的键列表,那么您可以索引到该键列表并获得您想要的。但是,除非您已经拥有它,否则您需要遍历地图以生成键列表,因此您仍然需要一个迭代器。

    【讨论】:

      【解决方案4】:

      一个老问题。

      好的,我在冒险:这可能是一种解决方法(虽然并不完美:这只是一种解决方法)。

      这篇文章有点长,因为我解释了为什么需要这样做。事实上,人们可能希望使用very same 代码从文件加载和保存数据。这对于同步数据的加载和保存、保持相同的数据、相同的顺序和相同的类型非常有用。

      例如,如果 op 是 load_op 或 save_op:

       load_save_data( var1, op );
       load_save_data( var2, op );
       load_save_data( var3, op );
       ...
      

      load_save_data 隐藏了里面执行的东西。维护因此变得更加容易。

      问题在于容器。例如(回到问题)它可以为集合(源 A)执行此操作以保存数据:

      int thesize = theset.size();
      load_save(thesize, load); // template (member) function with 1st arg being a typename
      for( elem: theset) {
         load_save_data( thesize, save_op );
       }
      

      但是,阅读(来源 B):

      int thesize;
      load_save_data( thesize, save);
      for( int i=0; i<thesize, i++) {
         Elem elem;
         load_save_data( elem, load_op);
         theset.insert(elem);
      }
      

      所以,整个源代码将是这样的,带有太多循环:

      if(op == load_op) { A } else { B }

      问题是有两种不同的循环,最好将它们合并为一种。理想情况下,能够这样做会很好:

      int thesize;
      load_save_data( thesize, save);
      for( int i=0; i<thesize, i++) {
         Elem elem;
         if( op == save_op ) {
            elem=theset[i]; // not possible
         }
         load_save_data( elem, op);
         if( op == load_op ) {
            theset.insert(elem);  
         }
      }
      

      (由于此代码在不同的上下文中使用,可能需要注意向编译器提供足够的信息,以允许它剥离不必要的代码(正确的“如果”),虽然不明显但可能)

      这样,对 load_save_data 的每次调用都以相同的顺序、相同的类型进行。您忘记了两个字段或一个字段都没有,但所有内容在保存和加载之间保持同步。您只能在一个地方添加变量、更改类型、更改顺序等。代码维护更容易。

      解决不可能的“theset[i]”确实是使用向量或映射而不是集合,但是您会丢失集合的属性(避免两个相同的项目)。

      因此,一种解决方法(但代价高昂:效率和简单性)类似于:

      void ...::load_save( op )
      {
          ...
         int thesize;
         set<...> tmp;
         load_save_data( thesize, save);
         for( int i=0; i<thesize, i++) {
            Elem elem;
            if( op == save_op ) {
               elem=*(theset.begin());      \
               theset.erase(elem);           >       <-----
               tmp.insert(elem);            /
            }
            load_save_data( elem, op);
            if( op == load_op ) {
               theset.insert(elem);  
            }
         }
         if(op == save_op) {
            theset.insert(tmp.begin(), tmp.end());   <-----
         }
         ...
      }
      

      不是很漂亮,但可以解决问题,而且(恕我直言)是最接近问题的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-17
        • 1970-01-01
        • 2020-11-17
        • 1970-01-01
        • 2013-02-20
        • 2011-03-18
        相关资源
        最近更新 更多