views:

588

answers:

6

Hi all,

I am a C++ newbie trying to use a map so I can get constant time lookups for the find() method.

The problem is that when I use an iterator to go over the elements in the map, elements do not appear in the same order that they were placed in the map.

Without maintaining another data structure, is there a way to achieve in order iteration while still retaining the constant time lookup ability?

Please let me know.

Thanks, jbu

edit: thanks for letting me know map::find() isn't constant time.

A: 

Items are ordreed by (by default) the operator< when applied to the key.

PS. std::map does not gurantee constant time look up.
It gurantees max complexity of O(ln(n))

Martin York
+2  A: 

First off, std::map isn't constant-time lookup. It's O(log n). Just thought I should set that straight.

Anyway, you have to specify your own comparison function if you want to use a different ordering. There isn't a built-in comparison function that can order by insertion time, but, if your object holds a timestamp field, you can arrange to set the timestamp at the time of insertion, and using a by-timestamp comparison.

Chris Jester-Young
And if not a timestamp, then a simple incrementing index.
Mark Ransom
+3  A: 

First of all, std::map guarantees O(log n) lookup time. You might be thinking about std::tr1::unordered_map. But that by definitions sacrifices any ordering to get the constant-time lookup.

You'd have to spend some time on it, but I think you can bash boost::multi_index_container to do what you want.

Kristo
In the Java world, there is a way to do amortised-constant time lookups, and still preserve insertion order, using a `LinkedHashMap` (space-time tradeoff). I wonder how easy that'd be to implement in C++.
Chris Jester-Young
@Chris: I'm pretty sure that it's about as easy as implementing `unordered_map`: in principle the list doesn't add a great deal of difficulty beyond what you're already doing to implement a hash. So not exactly "easy", but not challenging if you know what you're doing.
Steve Jessop
Yes, boost::multi_index_container should work nicely (once you wrap your head around its weird usage syntax).
Emile Cormier
+5  A: 

Without maintaining another data structure, is there a way to achieve in order iteration while still retaining the constant time lookup ability?

No, that is not possible. In order to get an efficient lookup, the container will need to order the contents in a way that makes the efficient lookup possible. For std::map, that will be some type of sorted order; for std::unordered_map, that will be an order based on the hash of the key.

In either case, the order will be different then the order in which they were added.

R Samuel Klatchko
+1 for the reason why things are arranged how they are.
GMan
A: 

Map is not meant for placing elements in some order - use vector for that.

If you want to find something in map you should "search" by the key using [the operator

If you need both: iteration and search by key see this topic

Draco Ater
Note that using `operator[]` has the side effect of creating an element if it does not exist. `find()` returns `map::end()` if the search failed.
mxp
actually people use maps all the time for placing things in order - just not in insertion order.
pm100
A: 

I'm going to actually... go backward.

If you want to preserve the order in which elements were inserted, or in general to control the order, you need a sequence that you will control:

  • std::vector (yes there are others, but by default use this one)

You can use the std::find algorithm (from <algorithm>) to search for a particular value in the vector: std::find(vec.begin(), vec.end(), value);.

Oh yes, it has linear complexity O(N), but for small collections it should not matter.

Otherwise, you can start looking up at Boost.MultiIndex as already suggested, but for a beginner you'll probably struggle a bit.

So, shirk the complexity issue for the moment, and come up with something that work. You'll worry about speed when you are more familiar with the language.

Matthieu M.