选择具有唯一性的STL容器并保持插入顺序

时间:2009-04-20 16:41:47

标签: c++ stl

在下列情况下,我无法决定使用哪个STL容器:

  1. 我想保留元素的插入顺序
  2. 容器中的元素必须是唯一的。
  3. 是否有可用的现成容器?我不想使用向量,然后在每次执行std::find之前执行push_back

8 个答案:

答案 0 :(得分:20)

Boost MultiIndex应该能够完全按照您的意愿执行 - 您只需使用一个有序索引即可获得“按插入顺序排序”的要求,以及hashed_uniqueordered_unique索引以获得唯一性要求。

答案 1 :(得分:5)

可能有一种很好的内置方法可以做到这一点,但一种相当简单的方法是同时使用hash_map和list。在每次插入之前检查hash_map,然后插入两者。你可能希望将它封装在一个类中,可能。

答案 2 :(得分:3)

没有标准的库容器可以直接提供您想要的内容。我将从一个std :: vector开始并编写一个自由函数来执行insert,它会为你执行find和push_back。如果这样就足够了,不要再进一步了。如果您遇到性能问题,请考虑更复杂的解决方案。

答案 3 :(得分:1)

你可以这样做:

  • 使用两个成员创建元素类的包装:元素和索引。我们称之为'InsertedElement'。索引将是插入订单。

  • 为此类定义比较运算符,该运算符仅考虑您的元素,但不考虑索引。这将确保元素的唯一性,同时记住它们的插入顺序。

  • 将std :: set和插入计数器包装在另一个类中。然后,当您要插入新元素时,可以:

  • 它已经存在,无所事事。
  • 它没有:在给它当前最大索引+ 1的情况下将其插入地图。

类似的东西:

class CMagicContainer
{
  public:
    std::set<InsertedElement> collection;
    int indexGenerator;

    ...
};

答案 4 :(得分:0)

在您选择的容器上实现一个包装类(例如list,vector,deque),并重写insert / push_back方法以在插入元素之前检查插入的元素是否唯一。

不幸的是,我不知道任何符合您标准的STL容器。

答案 5 :(得分:0)

正如其他人所说,没有一个STL容器可以做到这一点。我喜欢Neil Butterworth的回答。另一种选择是使用集合和向量。当你去插入时,检查元素是否在集合中。如果是,则无法插入,因为这会违反您的唯一性要求。如果不是,请将其添加到集合中,然后将其插入到矢量中。这很容易实现,可以包装成一个类。权衡是增加内存开销。但是你通过在单个向量上进行自己的查找来交换它以增加计算时间。这一切都取决于您在问题域中使用了多少数据以及您的时间和内存限制(如果有的话)。

答案 6 :(得分:0)

如果你已经安装了boost,我喜欢这个选项。否则,为什么不使用列表或向量并在插入时添加支票(find(k) == std::npos)?我认为它可能会在一个真正的,非常大的列表上变得缓慢,但对于大多数情况,它会工作得很好。

答案 7 :(得分:0)

好吧,我曾经有过类似的情况。在我脑海中浮现的一件事是“我可以使用多键”吗?我用谷歌搜索了一段时间,并使用std :: set找到了一个样本。所以,如果你没有获得提升(我推荐它,没有必要重新发明轮子);你可以尝试类似下面的例子。我认为您可以使用辅助键作为插入位置(自动增量)。从我在互联网上找到的例子;我根据自己的需求量身定制。这是同一版本的修改版本。

Cavaet:它正在使用宏。我知道他们是邪恶的一般,但这个用途是o.k.我想。

#include <set>
#include <functional>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>

#define MULTIKEYDEF(NAME,TYPE) \
    inline TYPE const & NAME() const { return d_##NAME; } \
    inline void NAME(TYPE const & t) { d_##NAME = t; } \
    TYPE d_##NAME; \
class T##NAME \
    : public std::unary_function<Tmultikey*,bool> { \
private: \
    TYPE d_compare; \
public: \
    T##NAME(TYPE t) : d_compare(t) {} \
    T##NAME(T##NAME const & self) \
    : d_compare(self.d_compare) {} \
    bool operator()(Tmultikey * mk) { \
    return d_compare == mk->##NAME(); \
    } \
   inline TYPE const& name() const { return d_compare; } \
    }

class Tmultikey {
public:
    // Actual keys
    // Can be accessed through d_primary and d_secondary,
    // or primary() and secondary()
    MULTIKEYDEF(primary , std::string);
    MULTIKEYDEF(secondary, unsigned int);
    // Mandatory
    bool operator < (Tmultikey const& mk) const {
        if(primary() < mk.primary()) return true;
        else if(primary() == mk.primary()) {
            return secondary() < mk.secondary();
        }
        return false;
    }
    // Constructor
    Tmultikey(std::string p, unsigned int s)
        : d_primary(p), d_secondary(s) {}

    // Eraser for std::set
    template<typename Compare>
        static void erase(std::set<Tmultikey> & c, Compare op) {
            typename std::set<Tmultikey>::iterator pos = c.begin();
            while(pos != c.end()) {
                if(op(&(*pos))) {
                    c.erase(pos++);
                } else ++pos;
            }
        }
      // Eraser for std::set
      template<typename Compare>
      static std::set<Tmultikey>::iterator find_ex(std::set<Tmultikey> & c, Compare op) {
         typename std::set<Tmultikey>::iterator pos = c.begin();
         while(pos != c.end()) {
            if(op(&(*pos))) {
               break;
            } else ++pos;
         }
         return pos;
      }
};

int main(int argc, char* argv[])
{
    std::set<Tmultikey> mkset;

    mkset.insert(Tmultikey("1",5));
    mkset.insert(Tmultikey("6",4));
    mkset.insert(Tmultikey("3",7));
    mkset.insert(Tmultikey("1",6));

   std::set<Tmultikey>::iterator bg = mkset.begin();
   for (;bg != mkset.end(); ++bg)
   {
      std::cout<<(*bg).primary()<<std::endl;
   }

    Tmultikey::erase(mkset,Tmultikey::Tsecondary(4));
    //Tmultikey::erase(mkset,Tmultikey::Tprimary("1"));

   std::cout<<"After erase ....\n";
   bg = mkset.begin();
   for (;bg != mkset.end(); ++bg)
   {
      std::cout<<(*bg).primary()<<std::endl;
   }
   bg = mkset.find(Tmultikey("3",7));
   if (bg != mkset.end())
   {
      std::cout<<"Absolute Find:"<<(*bg).primary()<<" "<<(*bg).secondary()<<std::endl;
   }
   //bg = Tmultikey::find_ex(mkset,Tmultikey::Tprimary("1"));
   bg = Tmultikey::find_ex(mkset,Tmultikey::Tsecondary(5));
   if (bg != mkset.end())
   {
      std::cout<<"Partial Find:"<<(*bg).primary()<<" "<<(*bg).secondary()<<std::endl;
   }
   else {
      std::cout<<"Partial Find: FAILED\n";
   }

   return 0;
}

HTH,