通过索引进行快速搜索,并在C ++中保持插入顺序

时间:2014-02-06 16:11:35

标签: c++ search boost containers boost-multi-index

我需要一个能够快速搜索超过100万件物品并保持插入顺序的容器。

所以首先我想到了std :: map,但它并不关心它根据键对数据进行排序的插入顺序。 我找到了boost :: multi_index,它将保留插入顺序并通过索引字段快速搜索数据(对于我的情况,这是id(不唯一!))。所以我做了类似的事情:

struct myData
{
    unsigned long id;
    unsigned long insertionOrder;

    myData (){};
    myData (unsigned long id_,unsigned long insertionOrder_):id(id_), insertionOrder(insertionOrder _)){}
    ~ myData (){};
};


typedef multi_index_container<
    myData, 
        indexed_by<    
            random_access<>,  // keep insertion order
            ordered_non_unique< member< myData, unsigned long, & myData::id> >
        > 
> myDataContainerType;

我可以毫无问题地将数据推送到容器中。假设我将5个项目插入到我的容器中,如:

myDataContainer.push_back(myData(1002, 1));
myDataContainer.push_back(myData(1001, 2));
myDataContainer.push_back(myData(1005, 3));
myDataContainer.push_back(myData(1003, 4));
myDataContainer.push_back(myData(1000, 5));

问题是当我在容器中搜索项目“1001”时,iterator++会返回"1002"iterator—会返回"1000"。所以它似乎并不关心插入顺序,也根据索引值对数据进行排序。

我希望结果为iterator++“1002”iterator--“1005”。我的意思是根据插入顺序的数据。

我做错了吗?如何通过索引值进行快速搜索并根据插入顺序检索数据。

我正在使用Visual Studio 2008,Visual C ++,Win 7 x64计算机。

3 个答案:

答案 0 :(得分:6)

你的boost::multi_index尝试几乎就在那里。问题是当你使用有序索引进行查找时,迭代的被排序。幸运的是,多索引提供了project机制来在索引之间切换。如果我正确阅读文档:

auto ordered_iter = myMap.find(1001);
auto iter = boost::multi_index::project<0>(ordered_iter);

答案 1 :(得分:1)

我会使用与multimap<Key,List<Item>::Iterator>配对的List<Item>。我会使用地图进行查找,List会按插入顺序保存项目。您需要在所有插入/更新/删除方案中使两个容器保持最新。如果您可以阐述您的用例,可能会有更好的选择。

此选项将为您提供log(n)查找,同时仍允许持续时间删除索引和项目。这类似于我过去实现LRU缓存的方式。

由于问题而编辑

typedef list<myData> DataLst;
typedef DataLst::iterator LstIter; 
typedef multimap<unsigned long, LstIter> mpType; 

mpType BuildIndex(DataLst &lst)
{
    mpType ret; 
    for (auto Item = begin(lst); Item != end(lst); Item++)
    {       
        ret.insert(make_pair(Item->id,Item));
    }
    return ret; 
}

int _tmain(int argc, _TCHAR* argv[])
{
    DataLst myDataContainer; 
    myDataContainer.push_back(myData(1002, 1));
    myDataContainer.push_back(myData(1001, 2));
    myDataContainer.push_back(myData(1005, 3));
    myDataContainer.push_back(myData(1003, 4));
    myDataContainer.push_back(myData(1000, 5));

    auto myMap = BuildIndex(myDataContainer);
    auto iter = myMap.find(1001);
    cout << "The iter insert  = " << iter->second->insertionOrder << endl;
    cout << "The iter insert after = " <<  std::next(iter->second)->insertionOrder << endl;
    cout << "The iter insert before = " << std::prev(iter->second)->insertionOrder << endl;
    string foo; 
    cin >> foo; 
}

输出

The iter insert  = 2
The iter insert after = 3
The iter insert before = 1

答案 2 :(得分:0)

是的,Mark B提供的内容完全正确。我只是想为未来可能的访问者提供正确的语法。

我创建了一个typedef:

typedef myDataContainerType::nth_index<1>::type myDataContainerType_by_id;

myDataContainerType myDataContainer;

以及根据id查找数据并将索引更改为插入顺序的语法:

myDataContainerType_by_id& idIndex = myContainer.get<1>();
myContainerType_by_id::iterator itId = idIndex.find(fId);

if (itId == idIndex.end())
    return 0;

myDataContainerType::const_iterator itInsertionOrder = myDataContainer.project<0>(itId);

// *** Alternative way to change index which works as well
myDataContainerType::const_iterator itInsertionOrder2 = myDataContainer.iterator_to(*itId);
// ***