基于std :: unordered_map(地图图)提升多索引容器与多级映射容器

时间:2014-10-20 14:20:23

标签: c++ boost std unordered-map boost-multi-index

我最近发现了boost :: multi_index_container,我对他的表现感到好奇,与我自己实现的基于多级映射的类似容器相比,定义为:

typedef int      Data;
typedef uint64_t MainKey;
typedef uint64_t SecondaryKey;

typedef std::unordered_map<SecondaryKey, Data>       SecondaryMap;
typedef std::unordered_map<PrimaryKey, SecondaryMap> PrimaryMap;

密钥排序并不重要。快速查找非常重要,我可以使用以下内容:

// find primaryKey=10 and secondaryKey=30
PrimaryMap m;
....
auto i1 = m.find( 10);
if ( i1 != m.end())
{
    auto& secondary = i1->second;
    auto i2 = secondary.find( 30);
    if ( i2 != secondary.end())
    {
        found = true;
        ....
    }
}

我想知道会是什么

  • 最接近的boost :: multi_index_container配置,以匹配我的实现
  • 按主键和辅助键搜索的最快方式。

我尝试配置模板,但我不确定这是否是最佳解决方案:

struct RecordKey
{
    MainKey         mainKey;
    SecondaryKey    secondaryKey;

    RecordKey( const MainKey mainKey, SecondaryKey secondaryKey):
        mainKey( mainKey),
        secondaryKey( secondaryKey)
    {}
};

struct Record: public RecordKey
{
    Data data;

    Record( const MainKey mainKey = 0, const SecondaryKey secondaryKey = 0, const Data& data = 0):
        RecordKey( mainKey, secondaryKey),
        data( data)
    {}
};


struct MainKeyTag {};
struct SecondaryKeyTag {};
struct CompositeKeyTag {};

using boost::multi_index_container;
using namespace boost::multi_index;

typedef boost::multi_index_container<Record,
    indexed_by  <   /*random_access<>,*/
                    hashed_non_unique<tag<MainKeyTag>,      BOOST_MULTI_INDEX_MEMBER( RecordKey, MainKey, mainKey) >,
                    hashed_non_unique<tag<SecondaryKeyTag>, BOOST_MULTI_INDEX_MEMBER( RecordKey, SecondaryKey, secondaryKey) >,
                    hashed_unique<tag<CompositeKeyTag>,     composite_key<Record,   member<RecordKey, MainKey, &RecordKey::mainKey>,
                                                                                    member<RecordKey, SecondaryKey, &RecordKey::secondaryKey> > > > > RecordContainer;

3 个答案:

答案 0 :(得分:8)

你可以通过选择BMI中的有序(而不是散列)索引来获得你的蛋糕并吃掉它。

有序复合索引的一个很好的属性允许您通过部分键进行查询,因此您只需要定义复合索引以便能够通过主索引进行查询:

typedef boost::multi_index_container<
    Record,
    indexed_by<
        ordered_non_unique< tag<CompositeKeyTag>, 
        composite_key<Record, 
                    member<RecordKey, MainKey, &RecordKey::mainKey>,
                    member<RecordKey, SecondaryKey, &RecordKey::secondaryKey>
    > > > > RecordContainer;

现在您可以按mainKey查询:

range = index.equal_range(10);

或者通过复合:

range = index.equal_range(boost::make_tuple(10,30));

背景:

这是一个完整的演示 Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>

using MainKey      = uint64_t;
using SecondaryKey = uint64_t;
using Data         = std::string;

struct RecordKey
{
    MainKey         mainKey;
    SecondaryKey    secondaryKey;

    RecordKey( const MainKey mainKey, SecondaryKey secondaryKey):
        mainKey( mainKey),
        secondaryKey( secondaryKey)
    {}
};

struct Record: public RecordKey
{
    Data data;

    Record( const MainKey mainKey = 0, const SecondaryKey secondaryKey = 0, const Data& data = 0):
        RecordKey( mainKey, secondaryKey),
        data( data)
    {}

    friend std::ostream& operator<<(std::ostream& os, Record const& r) {
        return os << " Record[" << r.mainKey << ", " << r.secondaryKey << ", " << r.data << "]";
    }
};

struct MainKeyTag {};
struct SecondaryKeyTag {};
struct CompositeKeyTag {};

using boost::multi_index_container;
using namespace boost::multi_index;

typedef boost::multi_index_container<
    Record,
    indexed_by<
        ordered_non_unique< tag<CompositeKeyTag>, 
        composite_key<Record, 
                    member<RecordKey, MainKey, &RecordKey::mainKey>,
                    member<RecordKey, SecondaryKey, &RecordKey::secondaryKey>
    > > > > RecordContainer;


int main()
{
    RecordContainer records;
    records.insert(Record(10, 20, "12"));
    records.insert(Record(10, 30, "13"));
    records.insert(Record(10, 30, "13 - need not be unique!"));
    records.insert(Record(30, 40, "34"));
    records.insert(Record(30, 50, "35"));
    records.insert(Record(50, 60, "56"));
    records.insert(Record(50, 70, "57"));
    records.insert(Record(70, 80, "78"));

    std::cout << "\nAll records:\n----------------------------------------------------------------------\n";
    for (auto const& r : records)
        std::cout << r << "\n";

    {
        std::cout << "\nAll records with (main) == (10):\n----------------------------------------------------------------------\n";
        auto& index = records.get<0>();
        auto range = index.equal_range(10);
        for (auto const& r : boost::make_iterator_range(range.first, range.second))
            std::cout << r << "\n";
    }

    {
        std::cout << "\nAll records with (main,secondary) == (10,30):\n----------------------------------------------------------------------\n";
        auto& index = records.get<0>();
        auto range = index.equal_range(boost::make_tuple(10,30));
        for (auto const& r : boost::make_iterator_range(range.first, range.second))
            std::cout << r << "\n";
    }
}

输出:

All records:
----------------------------------------------------------------------
 Record[10, 20, 12]
 Record[10, 30, 13]
 Record[10, 30, 13 - need not be unique!]
 Record[30, 40, 34]
 Record[30, 50, 35]
 Record[50, 60, 56]
 Record[50, 70, 57]
 Record[70, 80, 78]

All records with (main) == (10):
----------------------------------------------------------------------
 Record[10, 20, 12]
 Record[10, 30, 13]
 Record[10, 30, 13 - need not be unique!]

All records with (main,secondary) == (10,30):
----------------------------------------------------------------------
 Record[10, 30, 13]
 Record[10, 30, 13 - need not be unique!]

答案 1 :(得分:0)

多索引容器必须有3个索引:

  • 主键索引:hashed - 因为std::unordered_map经过哈希处理, non_unique - 因为多个记录可以具有相同的密钥;
  • 二级密钥索引:与主密钥相同;
  • 复合键的索引:hashed - 因为std::unordered_map经过哈希处理,unique - 因为元组(主键,辅助键)在地图映射中是唯一的 结构

容器定义为:

typedef boost::multi_index_container<Record, indexed_by <
    hashed_non_unique<tag<MainKeyTag>,      BOOST_MULTI_INDEX_MEMBER( RecordKey, MainKey, mainKey) >,
    hashed_non_unique<tag<SecondaryKeyTag>, BOOST_MULTI_INDEX_MEMBER( RecordKey, SecondaryKey, secondaryKey) >,
    hashed_unique<tag<CompositeKeyTag>,     
    composite_key<Record,
        member<RecordKey, MainKey, &RecordKey::mainKey>,
        member<RecordKey, SecondaryKey, &RecordKey::secondaryKey> > > > > RecordContainer;

插入是:

RecordContainer c;
c.insert( Record( 10, 20, 0));
c.insert( Record( 10, 30, 1));
c.insert( Record( 10, 40, 2));

查询是:

auto& index = c.get<CompositeKeyTag>();
auto pos = index.find( boost::make_tuple( 10, 30)); // don't use std::make_tuple!
if ( pos != index.end())
{
    found = true;
    data = pos->data;
}

答案 2 :(得分:0)

类似的情况,但是我的组中不允许使用以下方式实现提升,如果使用辅助键查找,则只需要一次查找而不是两次:

    #include<map>
    using namespace std;

    template<class PrimaryKey, class SecondaryKey, class Data>
    class MyMultiIndexedMap
    {
        private:
        typedef map<PrimaryKey, Data*> PT;
        typedef map<SecondaryKey, Data*> ST;

        PT pri_data_;
        ST sec_data_;

        public:
        bool insert(PrimaryKey pk, SecondaryKey sk, Data *data);
        Data* findBySecondaryKey(SecondaryKey sk);       
        Data* findByPrimaryKey(PrimaryKey pk);    
    };