I want to store certain objects in a HashMap. The problem is, usually you just use a single object as a key. (You can, for example, use a String.) What I want to do it to use multiple object. For example, a Class and a String. Is there a simple and clean way to implement that?
I tend to use a list
map.put(Arrays.asList(keyClass, keyString), value)
You could create a holder class that contains the class and string that you want as the keys.
public class Key {
public MyClass key_class;
public String key_string;
public Key(){
key_class = new MyClass();
key_string = "";
}
}
Probably not the best solution, but a possibility.
The easiest way that I know of is to make a wrapper class and override hashmap and equals. For instance:
public class KeyClass {
private String element1;
private String element2;
//boilerplate code here
public boolean equals(Object obj) {
if (obj instanceof KeyClass) {
return element1.equals(((KeyClass)obj).element1) &&
element2.equals(((KeyClass)obj).element2);
}
return false;
}
public int hashcode() {
return (element1 + element2).hashcode();
}
}
OF course, I would recommend using a StringBuilder and whatever else, but this way you have overridden the equals and hashcode, thereby allowing a hash and equality check on your multiple keys.
Also, I would recommend making the objects immutable (not editable) for safety's sake, but that is purely preference.
Do you mean that the object will be keyed by two keys, or rather a key which consists of two things.
If you want the first case. That is, an objected keyed by two keys, say a class or an object, you need to use two maps.
Map<Key1, value>
Map<Key2, value>
In the second case you need a map of maps, so:
Map<Key1, Map<Key2, value>>
You key must implement the hashCode and equals. If it is a SortedMap, it must also implements the Comparable interface
public class MyKey implements Comparable<MyKey>
{
private Integer i;
private String s;
public MyKey(Integer i,String s)
{
this.i=i;
this.s=s;
}
public Integer getI() { return i;}
public String getS() { return s;}
@Override
public int hashcode()
{
return i.hashcode()+31*s.hashcode();
}
@Override
public boolean equals(Object o)
{
if(o==this) return true;
if(o==null || !(o instanceof MyKey)) return false;
MyKey cp= MyKey.class.cast(o);
return i.equals(cp.i) && s.equals(cp.s);
}
public int compareTo(MyKey cp)
{
if(cp==this) return 0;
int i= i.compareTo(cp.i);
if(i!=0) return i;
return s.compareTo(cp.s);
}
@Override
public String toString()
{
return "("+i+";"+s+")";
}
}
public Map<MyKey,String> map= new HashMap<MyKey,String>();
map.put(new MyKey(1,"Hello"),"world");
Apache Commons Collections has a multikey map which might do the trick for you:
It looks like it will handle up to 5 "keys".
There are a few places where people suggest creating a "Key" class containing the others, I totally agree. Just thought I'd add a helpful hint.
If you use eclipse or netbeans, they have a nice option--you can tell Eclipse to create equals and hashcode methods based on one or more members. So you just select the member (or members) you want to retrieve by and NB creates most of the code you'd need to write for you.
of course when I just want to retrieve by one object, I often just delegate the hashcode and equals methods to that object (delegating equals might be problematic because it would mean that one of your "Key holder" classes would be equal to the object that is it's key, but that's pretty easily fixed (and wouldn't usually effect anything anyway)
so off the top of my head:
class KeyHolder {
public final String key;
public final Object storeMe;
public KeyHolder(String key, Object storeMe) {
this.key=key;
this.storeMe=storeMe;
}
public equals(Object o) {
return (o instanceof KeyHolder && ((KeyHolder)o).key.equals(key));
}
public hashcode() {
return key.hashCode();
}
}
That's all there is to it, and eclipse will do the last two for you if you ask it to.
By the way, I know that I have public members, a public final member is exactly the same thing as having a getter--not really a terrible idea. I'm starting to use this pattern on small utility classes like this a lot more lately. If the member wasn't final, it would be worse because it would be like having a setter (Something I try to avoid these days).