If I have the value "foo", and a hashmap ftw for which ftw.containsValue("foo")
returns true
, how can I get the corresponding key? Do I have to loop through the hashmap? What is the best way to do that?
views:
10988answers:
11There is no unambiguous answer, because multiple keys can map to the same value. If you are enforcing unique-ness with your own code, the best solution is to create a class that uses two Hashmaps to track the mappings in both directions.
To find all the keys that map to that value, iterate through all the pairs in the hashmap, using map.entrySet().
It sounds like the best way is for you to iterate over entries using map.entrySet() since map.containsValue() probably does this anyway.
If you choose to use the Commons Collections library instead of the standard Java Collections API, you can achieve this with ease.
The BidiMap interface in the Collections library is a bi-directional map, allowing your to map a key to a value (like normal maps), and also to map a value to a key, thus allowing you to perform lookups in both directions. Obtaining a key for a value is supported by the getKey() method.
There is a caveat though, bidi maps cannot have multiple values mapped to keys, and hence unless your data set has 1:1 mappings between keys and values, you cannot use bidimaps.
Update
If you want to rely on the Java Collections API, you will have to ensure the 1:1 relationship between keys and values at the time of inserting the value into the map. This is easier said than done.
Once you can ensure that, use the entrySet() method to obtain the set of entries (mappings) in the Map. Once you have obtained the set whose type is Map.Entry, iterate through the entries, comparing the stored value against the expected, and obtain the corresponding key.
Update #2
Support for bidi maps with generics can be found in Google Collections and the refactored Commons-Collections libraries (the latter is not an Apache project). Thanks to Esko for pointing out the missing generic support in Apache Commons Collections. Using collections with generics makes more maintainable code.
I think your choices are
- Use a map implementation built for this, like the BiMap from google collections. Note that the google collections BiMap requires uniqueless of values, as well as keys, but it provides high performance in both directions performance
- Manually maintain two maps - one for key -> value, and another map for value -> key
- Iterate through the entrySet() and to find the keys which match the value. This is the slowest method, since it requires iterating through the entire collection, while the other two methods don't require that.
If you build the map in your own code, try putting the key and value in the map together:
public class KeyValue {
public Object key;
public Object value;
public KeyValue(Object key, Object value) { ... }
}
map.put(key, new KeyValue(key, value));
Then when you have a value, you also have the key.
Yes, you have to loop through the hashmap, unless you implement something along the lines of what these various answers suggest. Rather than fiddling with the entrySet, I'd just get the keySet(), iterate over that set, and keep the (first) key that gets you your matching value. If you need all the keys that match that value, obviously you have to do the whole thing.
As Jonas suggests, this might already be what the containsValue method is doing, so you might just skip that test all-together, and just do the iteration every time (or maybe the compiler will already eliminate the redundancy, who knows).
Also, relative to the other answers, if your reverse map looks like
Map<Value, Set<Key>>
you can deal with non-unique key->value mappings, if you need that capability (untangling them aside). That would incorporate fine into any of the solutions people suggest here using two maps.
If your data structure has many to one mapping between keys and values you should iterate over all entries and pick suitable keys:
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
Set<T> keys = new HashSet<T>();
for (Entry<T, E> entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
keys.add(entry.getKey());
}
}
return keys;
}
I'm afraid you'll just have to iterate your map. Shortest I could come up with:
Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String,String> entry = iter.next();
if (entry.getValue().equals(value_you_look_for)) {
String key_you_look_for = entry.getKey();
}
}
You can get the key using values using following code..
ArrayList valuesList = new ArrayList();
Set keySet = initalMap.keySet();
ArrayList keyList = new ArrayList(keySet);
for(int i = 0 ; i < keyList.size() ; i++ )
{
valuesList.add(initalMap.get(keyList.get(i)));
}
Collections.sort(valuesList);
Map finalMap = new TreeMap();
for(int i = 0 ; i < valuesList.size() ; i++ )
{
String value = (String) valuesList.get(i);
for( int j = 0 ; j < keyList.size() ; j++ )
{
if(initalMap.get(keyList.get(j)).equals(value))
{
finalMap.put(keyList.get(j),value);
}
}
}
System.out.println("fianl map ----------------------> " +finalMap);