I would like to use an object that behaves like a hashmap (ie. put,
get, with a key), but I'd like to be able to index items in that
object by more than one key.
Is there a way to do this?
Ie. if I had a name for the objects I'm adding as well as a social
security number, I'd like to be able to return the object by either
name or number depending on need. Is there an object that does this,
or do I have to write it myself?
There are two main strategies I know of, and you can blend
them in various degrees:
1) Use multiple Maps, one for each key. Your example would
have a Map<Name,Thing> and a Map<SSN,Thing>. Each time you add
a Thing to one Map you also add it to the other; same business
on deletions. It may be convenient to gather all these Maps
into one big MegaMap, so you can more easily produce things
like Iterators that support remove(). See "inverted file."
(I doubt that a MegaMap could easily implement Map, since the
keySet() method would be problematic. But maybe your problem
suggests some domain-specific approach to that difficulty.)
2) Use a single Map<Object,Thing>, entering each Thing
into the Map once for each of its keys. If the keys are easily
distinguished (e.g., "Mortimer Snerd" is easily recognizable as
a Name and 011-22-4343 as an SSN), you might just use the keys
as they stand. If not, it might be best to make yourself a
little TaggedKey class holding the key's value along with an
enum designating its type:
class TaggedKey {
enum KeyType { NAME, SSN }
private final KeyType type;
private Object value;
}
.... and use a Map<TaggedKey,Thing>. See "library card catalog."
Note that an Iterator over the values() of such a Map will
encounter each Thing as many times as it has keys, just as
"The Sot-Weed Factor" appears many times in the library's catalog.
It will often turn out that not all the "keys" satisfy the
properties one wants in a key, properties like uniqueness. In
your example, Name is unlikely to be a unique key and even SSN
has problems. If so, you may want to synthesize a "primary key"
of some kind (this is why you likely have an "employee number")
and use one Map<PrimaryKey,Thing> for it, and use additional
Map<SecondaryKey,Collection<Thing>> maps for the others. (As
a refugee from the Bad Old Small-Memory days, I might instead
use a Map<SecondaryKey,Object> and store a mixture of Things
and Collection<Thing>s, using `instanceof Thing' at run-time
to figure out what I'd retrieved, and earning the scorn of
every modern-era right-thinking Java zealot.[*]) See also
TAOCP Volume III, "Retrieval on Secondary Keys."
[*] No offense, folks. But I recall a respected senior
programmer/architect at a former job whose response to "Why
worry? Memory's cheap" was to extend his hand, palm upwards,
and wait for the speaker to give him some. Oddly, nobody
ever did ...