C ++ STL map我不想让它排序!

时间:2010-02-15 13:23:43

标签: c++ stl

这是我的代码

map<string,int> persons;

persons["B"] = 123;
persons["A"] = 321;


for(map<string,int>::iterator i = persons.begin();
    i!=persons.end();
    ++i)
{
    cout<< (*i).first << ":"<<(*i).second<<endl;
}

预期产出:

  B:123
  A:321

但它给出的输出是:

  A:321
  B:123

我希望它能够维护map<string,int>中插入键和值的顺序。

有可能吗?或者我应该使用其他一些STL数据结构?哪一个?

20 个答案:

答案 0 :(得分:37)

没有标准容器可以直接执行您想要的操作。如果要维护插入顺序,要使用的明显容器是向量。如果您还需要按字符串查找,请使用矢量和地图。地图通常是字符串到矢量索引,但由于您的数据已经是整数,您可能只想复制它,具体取决于您的用例。

答案 1 :(得分:20)

就像Matthieu在另一个回答中所说,Boost.MultiIndex library似乎是你想要的正确选择。但是,这个库在开始时可能有点难以使用,特别是如果你没有很多C ++经验的话。以下是如何使用库来解决问题代码中的确切问题:

struct person {
    std::string name;
    int id;
    person(std::string const & name, int id) 
    : name(name), id(id) { 
    }
};

int main() {

    using namespace::boost::multi_index;
    using namespace std;

    // define a multi_index_container with a list-like index and an ordered index
    typedef multi_index_container<
      person,        // The type of the elements stored
      indexed_by<    // The indices that our container will support
        sequenced<>,                           // list-like index
        ordered_unique<member<person, string, 
                              &person::name> > // map-like index (sorted by name)
      >
    > person_container;

    // Create our container and add some people
    person_container persons;
    persons.push_back(person("B", 123));
    persons.push_back(person("C", 224));
    persons.push_back(person("A", 321));

    // Typedefs for the sequence index and the ordered index
    enum { Seq, Ord };
    typedef person_container::nth_index<Seq>::type persons_seq_index;
    typedef person_container::nth_index<Ord>::type persons_ord_index;

    // Let's test the sequence index
    persons_seq_index & seq_index = persons.get<Seq>();
    for(persons_seq_index::iterator it = seq_index.begin(), 
                                    e = seq_index.end(); it != e; ++it)
        cout << it->name << ":"<< it->id << endl;
    cout << "\n";

    // And now the ordered index
    persons_ord_index & ord_index = persons.get<Ord>();
    for(persons_ord_index::iterator it = ord_index.begin(), 
                                    e = ord_index.end(); it != e; ++it)
        cout << it->name << ":"<< it->id << endl;
    cout << "\n";

    // Thanks to the ordered index we have fast lookup by name:
    std::cout << "The id of B is: " << ord_index.find("B")->id << "\n";
}

产生以下输出:

B:123
C:224
A:321

A:321
B:123
C:224

The id of B is: 123

答案 2 :(得分:9)

地图绝对不适合你:

“在内部,地图中的元素按照在构造时设置的特定严格弱排序标准从较低到较高的键值排序。”

引自here

不幸的是,STL中没有无序的关联容器,所以要么使用像vector这样的非关联容器,要么自己编写: - (

答案 3 :(得分:4)

地图和集合意味着对数据施加严格的弱排序。 Strick弱排序保持没有条目equavalent(不同于等)。

您需要提供地图/集合可用于执行a<b的仿函数。使用这个仿函数,map / set对其项目进行排序(在GCC的STL中,它使用红黑树)。如果!a<b && !b<a - 平等测试,它确定天气两个项是等价的。

仿函数如下:

template <class T>
struct less : binary_function<T,T,bool> {
  bool operator() (const T& a, const T& b) const {
    return a < b;
  }
};

如果您可以提供告诉STL如何订购的功能,那么地图和集合可以做您想要的。例如

template<typename T>
struct ItemHolder
{
    int insertCount;
    T item;
};

然后,您可以通过insertCount轻松编写一个仿函数。如果您的实现使用red-black trees,您的基础数据将保持平衡 - 但是您将获得大量的重新平衡,因为您的数据将基于增量排序(相对于随机)生成 - 在这种情况下为{使用list的{​​1}}会更好。但是,您无法像使用地图/集一样快地按键访问数据。

如果你想按字符串排序 - 提供按字符串搜索的仿函数,使用insertCount你可以反向工作。如果你想两者都搜索,你可以有两张地图。

push_back

我经常使用这种策略 - 但是我从未将它放在经常运行的代码中。我正在考虑boost::bimap

答案 4 :(得分:4)

除了Neil建议的组合向量+映射,如果您需要保持插入顺序和按键搜索的能力,您还可以考虑使用boost多索引库,它提供可以多种方式寻址的容器。

答案 5 :(得分:2)

我偶尔会遇到同样的问题,这是我的解决方案:https://github.com/nlohmann/fifo_map。它是一个仅限标头的C ++ 11解决方案,可以用作std::map的替代品。

对于您的示例,可以按如下方式使用:

#include "fifo_map.hpp"
#include <string>
#include <iostream>

using nlohmann::fifo_map;

int main()
{
    fifo_map<std::string,int> persons;

    persons["B"] = 123;
    persons["A"] = 321;

    for(fifo_map<std::string,int>::iterator i = persons.begin();
        i!=persons.end();
        ++i)
    {
        std::cout<< (*i).first << ":"<<(*i).second << std::endl;
    }
}

然后输出

B:123
A:321

答案 6 :(得分:2)

嗯,没有STL容器实际上可以做你想要的,但有可能。

<强> 1。 STL

默认情况下,使用vector。这意味着:

struct Entry { std::string name; int it; };

typedef std::vector<Entry> container_type;

如果您希望按字符串搜索,则始终可以使用find算法。

class ByName: std::unary_function<Entry,bool>
{
public:
  ByName(const std::string& name): m_name(name) {}
  bool operator()(const Entry& entry) const { return entry.name == m_name; }
private:
  std::string m_name;
};

// Use like this:
container_type myContainer;
container_type::iterator it =
  std::find(myContainer.begin(), myContainer.end(), ByName("A"));

<强> 2。 Boost.MultiIndex的

这似乎有点矫枉过正,但您可以随时查看here

它允许您创建一个存储容器,可通过各种样式的各种索引访问,所有这些都为您(几乎)神奇地维护。

不是使用一个容器(std::map)来引用存储容器(std::vector),而是导致它导致的所有同步问题......最好使用Boost。

答案 7 :(得分:1)

您可以使用成对向量,它几乎与未排序的地图容器相同

std::vector<std::pair<T, U> > unsorted_map;

答案 8 :(得分:1)

为了保留所有时间复杂度约束,您需要map + list:

struct Entry
{
   string key;
   int val;
};

typedef list<Entry> MyList;
typedef MyList::iterator Iter;
typedef map<string, Iter> MyMap;

MyList l;
MyMap m;

int find(string key)
{
   Iter it = m[key]; // O(log n)
   Entry e = *it;
   return e.val;
}

void put(string key, int val)
{
   Entry e;
   e.key = key;
   e.val = val;
   Iter it = l.insert(l.end(), e); // O(1)
   m[key] = it;                    // O(log n)
}

void erase(string key)
{
   Iter it = m[key];  // O(log n)
   l.erase(it);       // O(1)
   m.erase(key);      // O(log n)
}

void printAll()
{
   for (Iter it = l.begin(); it != l.end(); it++)
   {
       cout<< it->key << ":"<< it->val << endl;
   }
}

享受

答案 9 :(得分:1)

使用矢量。它使您可以完全控制订购。

答案 10 :(得分:0)

我投票给typedef std::vector< std::pair< std::string, int > > UnsortedMap;

分配看起来有点不同,但你的循环仍然与现在完全一样。

答案 11 :(得分:0)

Map是有序集合(模板中的第二个参数是一个order functor),如set。如果你想将序列中的元素弹出为pushd,你应该使用deque或list或vector。

答案 12 :(得分:0)

为了完成他们的工作并提高效率,地图使用哈希表和排序。因此,如果您愿意放弃插入顺序的内存以获得按键查找的便利性和性能,则可以使用地图。

如果您需要存储插入订单,一种方法是创建一个新类型,将您存储的值与您存储的订单配对(您需要编写代码来跟踪订单) 。然后,您将使用字符串映射到此新类型进行存储。使用键执行查找时,还可以检索插入顺序,然后根据插入顺序对值进行排序。

还有一件事:如果您正在使用地图,请注意以下事实:测试人员[“C”]是否存在(仅在您插入A和B之后)将实际插入一个键值对地图。

答案 13 :(得分:0)

是的,地图容器不适合你。
如您所知,您需要以下代码:

   struct myClass {
      std::string stringValue;
      int         intValue;
      myClass( const std::string& sVal, const int& iVal ):
               stringValue( sVal ),
               intValue( iVal) {}
   };

   std::vector<myClass> persons;

   persons.push_back( myClass( "B", 123 ));
   persons.push_back( myClass( "A", 321 ));


   for(std::vector<myClass>::iterator i = persons.begin();
      i!=persons.end();
      ++i)
   {
      std::cout << (*i).stringValue << ":" << (*i).intValue << std::endl;
   }

此处输出未按预期排序。

答案 14 :(得分:0)

在Map中插入时,使用Map和迭代器矢量。 (保证映射迭代器不会失效)

在下面的代码中我使用的是Set 设置myset; 矢量:迭代&GT; VEC;

void printNonDuplicates(){     vector :: iterator&gt; :: iterator vecIter;     for(vecIter = vec.begin(); vecIter!= vec.end(); vecIter ++){         COUT&LT;≤(* vecIter) - &GT; c_str()&LT;

void insertSet(string str){     一对:迭代,布尔&GT; RET;     ret = myset.insert(str);     如果(ret.second)         vec.push_back(ret.first); }

答案 15 :(得分:0)

``struct Compare:public binary_function {   bool operator()(int a,int b){return true;} };

使用它来按照您输入的相反顺序获取地图的所有元素(即:第一个输入的元素将是最后一个,最后输入的元素将是第一个)。不如同一个订单,但它可能会带来一些不便。

答案 16 :(得分:0)

您可以使用带矢量的配对功能代替地图! 例如:

vector<::pair<unsigned,string>> myvec;
myvec.push_back(::pair<unsigned,string>(1,"a"));
myvec.push_back(::pair<unsigned,string>(5,"b"));
myvec.push_back(::pair<unsigned,string>(3,"aa"));`

输出:

myvec[0]=(1,"a"); myvec[1]=(5,"b"); myvec[2]=(3,"aa");

或其他前:

vector<::pair<string,unsigned>> myvec2;
myvec2.push_back(::pair<string,unsigned>("aa",1));
myvec2.push_back(::pair<string,unsigned>("a",3));
myvec2.push_back(::pair<string,unsigned>("ab",2));

输出:myvec2[0]=("aa",1); myvec2[1]=("a",3); myvec2[2]=("ab",2);

希望这可以帮助将来寻找像我这样的非分类地图的其他人!

答案 17 :(得分:0)

我也认为地图不是要走的路。 Map中的键形成一个Set;单个密钥只能出现一次。在地图中插入期间,地图必须搜索密钥,以确保密钥不存在或更新该密钥的值。为此,重要的(性能明智的)密钥以及条目具有某种排序。因此,插入排序的Map在插入和检索条目时效率非常低。

另一个问题是如果你使用相同的密钥两次;应保留第一个或最后一个条目,是否应更新插入顺序?

因此,我建议您使用Neils建议,插入时间排序向量和基于键的搜索映射。

答案 18 :(得分:0)

您可以查看std::unordered_map。从第一个角度来看,它看起来可能会解决您的问题。

答案 19 :(得分:0)

如果您不想使用Boost :: multi_index,我将概念证明类模板放在此处进行审核:

https://codereview.stackexchange.com/questions/233157/wrapper-class-template-for-stdmap-stdlist-to-provide-a-sequencedmap-which

使用std :: map和std :: list使用指针维护顺序。

删除将花费O(n)个线性时间,因为它需要在整个列表中搜索正确的指针。为了避免这种情况,需要在地图中使用另一个交叉引用。