12

I need to erase elements from an std::map based on the time of insertion (or something else more efficient than that).

The map will probably hold thousands of elements and if I store the time and iterate the map to check each elements time, it will probably end up being quite time consuming.

Does anyone have any good idea how to erase elements from a std::map when they are getting old?

theAlse
  • 5,217
  • 10
  • 57
  • 101
  • 1
    you might want to have a look at boost multi index containers – PlasmaHH Mar 15 '12 at 14:46
  • 1
    Old? You need a definite criteria to perform an action, unless you define one, the Q is pretty much directionless. – Alok Save Mar 15 '12 at 14:47
  • @PlasmaHH yeah boost would have been not but not possible to use in this project – theAlse Mar 15 '12 at 14:56
  • @Alborz: I hear that so often, I wonder if anyone is using boost at all... – PlasmaHH Mar 15 '12 at 15:00
  • *"or something else more efficient than that*" - So what, based on the insertion time or not? If not, I propose it to be based on being the first element in the map, would be more efficient than searching the map ;) – Christian Rau Mar 15 '12 at 15:06

3 Answers3

7

The std::map<> type has no notion of when an element was inserted. It only serves to hold a key / value pair mapping. It also has no notion of insert order so it can't even provide a relative type of insert.

To do what you want you'll need to add an association between the elements and the time they were inserted. If all you want is relative order then you could use a std::queue paired with the map. Every time you insert into the map you insert into the std::queue as well. Elements at front of the queue are older than the back and you can use that for relative age

JaredPar
  • 673,544
  • 139
  • 1,186
  • 1,421
6

Pretty close to LRU Cache.

The Boost.MultiIndex library shows an example of MRU Cache (Most Recently Used), so adapting it to LRU should be trivial.

Basically the idea is to maintain two data structures in parallel:

  • a map with the items in
  • a deque with references into the map

Basic code:

static double const EXPIRY = 3600; // seconds

std::map<Key, Value> map;
std::deque<std::pair<std::map<Key, Value>::iterator, time_t>> deque;

bool insert(Key const& k, Value const& v) {
  std::pair<std::map<Key, Value>::iterator, bool> result =
    map.insert(std::make_pair(k, v));

  if (result.second) {
    deque.push_back(std::make_pair(result.first, time()));
  }

  return result.second;
}

// to be launched periodically
void clean() {
  while (not deque.empty() and difftime(time(), deque.front().second) > EXPIRY) {
    map.erase(deque.front().first);
    deque.pop_front();
  }
}

Of course, those structures need be synchronized if the intent is to get multi-threaded code.

Matthieu M.
  • 251,718
  • 39
  • 369
  • 642
1

You can use a queue, and insert pointers to objects as they are inserted into the map. The next item in the queue will be the oldest one. Or you can store a pair in the queue if you also need the time of insertion.

perreal
  • 85,397
  • 16
  • 134
  • 168
  • Iterators might be more useful than pointers, but neither will be affected by insertions and erasures to the map: http://stackoverflow.com/a/6438087/5987 – Mark Ransom Mar 15 '12 at 16:39