我希望实现一个维护条目的插入顺序的多地图,并允许就地插入/替换而不影响订单。 Guava的LinkedListMultimap
几乎是完美的,但不允许我正在寻找的替换类型。 LinkedListMultimap
实现为哈希映射和多个链表;它看起来像这样:
________________
/ \
(A,1) -> (B,2) -> (A,3) -> (C,4) -> (B,5) -> (C,6) -> (A,7)
\________\_______/\________________/_________________/
\_______________________/
在内部,每个节点都有一个指向序列中下一个节点的指针,以及具有相同键的下一个节点,并且哈希表维护从键到具有该键的第一个节点的映射。
不幸的是,这不允许有效的就地插入或替换。例如,要将(C,4)
替换为(B,8)
,我必须向后走一条任意长的路才能找到(B,2)
,以便更新其“下一个相同的键”指针。
我最好的想法是将每个元素与序列号相关联,并为每个键保留一个有序集。但是要插入序列的中间,我需要无限可分的序列号。
(顺便说一下,我在C ++中实现这一点,但我只是在寻找一个可以工作的数据结构的描述。如果有一个预先存在的库可以工作那将是很好的,但甚至boost::multi_index_container
似乎无法完成任务。)
答案 0 :(得分:0)
回答#1
为什么Boost.MultiIndex在这里没有帮助你?
<强> Live On Coliru 强>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/member.hpp>
using namespace boost::multi_index;
#include <iosfwd>
template<typename T,typename Q>
struct pair_
{
T first;
Q second;
};
template<typename T,typename Q>
std::ostream& operator<<(std::ostream& os,const pair_<T,Q>& p)
{
return os<<"("<<p.first<<","<<p.second<<")";
}
template<typename T,typename Q>
using list_multimap=multi_index_container<
pair_<T,Q>,
indexed_by<
sequenced<>,
ordered_non_unique<
composite_key<
pair_<T,Q>,
member<pair_<T,Q>,T,&pair_<T,Q>::first>,
member<pair_<T,Q>,Q,&pair_<T,Q>::second>
>
>
>
>;
template<typename T,typename Q>
std::ostream& operator<<(std::ostream& os,const list_multimap<T,Q>& lmm)
{
for(const auto& p:lmm)os<<p<<" ";
return os;
}
#include <string>
#include <iostream>
int main()
{
list_multimap<std::string,int> lmm{{"A",1},{"B",2},{"A",3},{"C",4},{"B",5},{"C",6},{"A",7}};
auto& mm=lmm.get<1>();
std::cout<<lmm<<"\n";
// List values with key "A"
auto r=mm.equal_range("A");
while(r.first!=r.second)std::cout<<*(r.first)++<<" ";
std::cout<<"\n";
// replace (C,4) with (B,8)
mm.replace(mm.find(std::make_tuple("C",4)),{"B",8});
std::cout<<lmm<<"\n";
}
答案 1 :(得分:0)
回答#2
我的第一个答案可以通过改进来得到你想要的东西,我想:
<强> Live On Coliru 强>
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <functional>
using namespace boost::multi_index;
#include <iosfwd>
template<typename T,typename Q>
struct pair_
{
T first;
Q second;
using compare=std::function<bool(const pair_&,const pair_&)>;
mutable compare* subcmp;
pair_(const T& first,const Q& second,compare* subcmp=nullptr):
first(first),second(second),subcmp(subcmp){}
};
namespace std{
template<typename T,typename Q>
struct less<pair_<T,Q>>
{
bool operator()(const pair_<T,Q>& x,const pair_<T,Q>& y)const
{
if(x.first<y.first)return true;
if(y.first<x.first)return false;
if(x.subcmp) return (*x.subcmp)(x,y);
if(y.subcmp) return (*y.subcmp)(x,y);
return false;
}
template<typename R>
bool operator()(const R& x,const pair_<T,Q>& y)const
{
return x<y.first;
}
template<typename R>
bool operator()(const pair_<T,Q>& x,const R& y)const
{
return x.first<y;
}
};
} // namespace std
template<typename T,typename Q>
std::ostream& operator<<(std::ostream& os,const pair_<T,Q>& p)
{
return os<<"("<<p.first<<","<<p.second<<")";
}
template<typename T,typename Q>
using list_multimap=multi_index_container<
pair_<T,Q>,
indexed_by<
random_access<>,
ordered_non_unique<identity<pair_<T,Q>>>
>
>;
template<typename T,typename Q>
std::ostream& operator<<(std::ostream& os,const list_multimap<T,Q>& lmm)
{
for(const auto& p:lmm)os<<p<<" ";
return os;
}
#include <string>
#include <iostream>
int main()
{
list_multimap<std::string,int> lmm{{"A",1},{"B",2},{"A",3},{"C",4},{"B",5},{"C",6},{"A",7}};
auto& mm=lmm.get<1>();
std::cout<<lmm<<"\n";
// list values with key "A"
auto r=mm.equal_range("A");
while(r.first!=r.second)std::cout<<*(r.first)++<<" ";
std::cout<<"\n";
// replace (C,4) with (B,8)
pair_<std::string,int>::compare subcmp=[&](const auto&x, const auto& y){
auto itx=lmm.iterator_to(x);
auto ity=lmm.iterator_to(y);
return itx<ity;
};
r=mm.equal_range("C");
auto it=std::find_if(r.first,r.second,[](const auto& x){return x.second==4;});
mm.modify(it,[&](auto&x){x={"B",8,&subcmp};});
it->subcmp=nullptr;
std::cout<<lmm<<"\n";
// list values with key "B"
r=mm.equal_range("B");
while(r.first!=r.second)std::cout<<*(r.first)++<<" ";
std::cout<<"\n";
}
关键的想法是:
subcmp
中,这是可选的(如果subcmp
为空)。modify
(以便更改元素)并提供一个子编译器,它只是简单地考虑随机访问索引中元素的顺序。修改完成后,将subcmp
设置为nullptr
,因为不再需要它。答案 2 :(得分:0)
回答#3
我的第二个答案可以进一步细化,以将子比较器放在less<pair_<T,Q>>
对象本身内:
<强> Live On Coliru 强>
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <functional>
using namespace boost::multi_index;
#include <iosfwd>
template<typename T,typename Q>
struct pair_
{
T first;
Q second;
};
namespace std{
template<typename T,typename Q>
struct less<pair_<T,Q>>
{
using subcompare=std::function<bool(const pair_<T,Q>&,const pair_<T,Q>&)>;
subcompare subcmp;
bool operator()(const pair_<T,Q>& x,const pair_<T,Q>& y)const
{
if(x.first<y.first)return true;
if(y.first<x.first)return false;
if(subcmp) return subcmp(x,y);
return false;
}
template<typename R>
bool operator()(const R& x,const pair_<T,Q>& y)const
{
return x<y.first;
}
template<typename R>
bool operator()(const pair_<T,Q>& x,const R& y)const
{
return x.first<y;
}
};
} // namespace std
template<typename T,typename Q>
std::ostream& operator<<(std::ostream& os,const pair_<T,Q>& p)
{
return os<<"("<<p.first<<","<<p.second<<")";
}
template<typename T,typename Q>
using list_multimap=multi_index_container<
pair_<T,Q>,
indexed_by<
random_access<>,
ordered_non_unique<
identity<pair_<T,Q>>,
std::reference_wrapper<const std::less<pair_<T,Q>>>>
>
>;
template<typename T,typename Q>
std::ostream& operator<<(std::ostream& os,const list_multimap<T,Q>& lmm)
{
for(const auto& p:lmm)os<<p<<" ";
return os;
}
#include <string>
#include <iostream>
int main()
{
std::less<pair_<std::string,int>> less;
list_multimap<std::string,int> lmm{boost::make_tuple(
boost::make_tuple(),
boost::make_tuple(
identity<pair_<std::string,int>>{},
std::cref(less)
)
)};
auto& mm=lmm.get<1>();
lmm={{"A",1},{"B",2},{"A",3},{"C",4},{"B",5},{"C",6},{"A",7}};
std::cout<<lmm<<"\n";
// list values with key "A"
auto r=mm.equal_range("A");
std::for_each(r.first,r.second,[](const auto& x){std::cout<<x<<" ";});
std::cout<<"\n";
// replace (C,4) with (B,8)
std::less<pair_<std::string,int>>::subcompare subcmp=
[&](const auto&x, const auto& y){
return lmm.iterator_to(x)<lmm.iterator_to(y);
};
r=mm.equal_range("C");
auto it=std::find_if(r.first,r.second,[](const auto& x){return x.second==4;});
less.subcmp=subcmp;
mm.modify(it,[](auto& x){x={"B",8};});
less.subcmp=nullptr;
std::cout<<lmm<<"\n";
// list values with key "B"
r=mm.equal_range("B");
std::for_each(r.first,r.second,[](const auto& x){std::cout<<x<<" ";});
std::cout<<"\n";
}
这使我们大大减少了内存使用量,因为元素本身不需要为subcmp
添加额外的指针。总体战略仍然完全相同。