C++ standard library doesn't have a linked hash table container out of the box.
This kind of container introduces additional memory overhead to save insertion order, which is not needed in most of common scenarios.
This behavior doesn't satisfy a common C++ concept: "don't pay for something you don't use".
At the same time, if you still need to save insertions order you can trivially combine std::list
- doubly linked list and unordered_map
- hash table in a single class.
For example:
#include <iostream>
#include <list>
#include <unordered_map>
template<typename K, typename V, typename H = std::hash<K> >
class linked_hash_map
{
private:
typedef std::list<V> holder_container_t;
public:
typedef typename holder_container_t::iterator iterator;
typedef typename holder_container_t::const_iterator const_iterator;
private:
typedef typename std::unordered_map<K, iterator> access_container_t;
public:
linked_hash_map():
holder_(),
index_()
{}
std::pair<iterator, bool> insert( const std::pair<K,V>& entry)
{
holder_.push_back( entry.second );
auto idx_insert = index_.insert( std::make_pair(entry.first, --holder_.end() ) );
if(!idx_insert.second) {
holder_.pop_back();
return std::make_pair(holder_.end(),false);
}
else {
return std::make_pair(--holder_.end(),true);
}
}
iterator erase(const K& key)
{
auto ret = index_.find(key);
if(ret == index_.end()) {
return end();
}
index_.erase(ret);
return holder_.erase( ret->second );
}
bool contains(const K& k) const
{
return index_.contains(k);
}
iterator find(const K& key)
{
auto ret = index_.find(key);
return ( ret == index_.end() ) ? end() : ret->second;
}
const_iterator find(const K& key) const
{
auto idx = index_.find(key);
return idx == index_.cend() ? cend() : *idx;
}
iterator begin()
{
return holder_.begin();
}
iterator end()
{
return holder_.end();
}
const_iterator cbegin() const
{
return holder_.cbegin();
}
const_iterator cend() const
{
return holder_.cend();
}
private:
holder_container_t holder_;
access_container_t index_;
};
int main()
{
linked_hash_map<std::string, std::size_t> lhm;
// fill a map in a reverse order
for(int i=10; i >= 0 ; i--) {
auto key = std::string("Test") + std::to_string(i);
lhm.insert( std::make_pair( key, i ) );
}
// look up for elements
auto sret = lhm.find("Test5");
if( lhm.end() != sret )
std::cout << "Test5 = " << *sret << std::endl;
sret = lhm.find("Test7");
if( lhm.end() != sret )
std::cout << "Test7 = " << *sret << std::endl;
lhm.erase("Test5");
sret = lhm.find("Test5");
if( lhm.end() != sret )
std::cout << "Test5 = " << *sret << std::endl;
else
std::cout << "Test5 not found" << std::endl;
// Iterate over the map by values in their insert order
for(auto it: lhm) {
std::cout<< it << ' ';
}
return 0;
}