我需要一个能够快速搜索超过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计算机。
答案 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);
// ***