Java Hashmap: How to get key from value?

1,047,150

Solution 1

If you choose to use the Commons Collections library instead of the standard Java Collections framework, you can achieve this with ease.

The BidiMap interface in the Collections library is a bi-directional map, allowing you 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 bidi maps.


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.


Support for bidi maps with generics can be found in Google Guava 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.


Since version 4.0 the official Apache Commons Collections™ library supports generics.

See the summary page of the "org.apache.commons.collections4.bidimap" package for the list of available implementations of the BidiMap, OrderedBidiMap and SortedBidiMap interfaces that now support Java generics.

Solution 2

If your data structure has many-to-one mapping between keys and values you should iterate over entries and pick all 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 (Objects.equals(value, entry.getValue())) {
            keys.add(entry.getKey());
        }
    }
    return keys;
}

In case of one-to-one relationship, you can return the first matched key:

public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
    for (Entry<T, E> entry : map.entrySet()) {
        if (Objects.equals(value, entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}

In Java 8:

public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
    return map.entrySet()
              .stream()
              .filter(entry -> Objects.equals(entry.getValue(), value))
              .map(Map.Entry::getKey)
              .collect(Collectors.toSet());
}

Also, for Guava users, BiMap may be useful. For example:

BiMap<Token, Character> tokenToChar = 
    ImmutableBiMap.of(Token.LEFT_BRACKET, '[', Token.LEFT_PARENTHESIS, '(');
Token token = tokenToChar.inverse().get('(');
Character c = tokenToChar.get(token);

Solution 3

public class NewClass1 {

    public static void main(String[] args) {
       Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");
        for (Entry<Integer, String> entry : testMap.entrySet()) {
            if (entry.getValue().equals("c")) {
                System.out.println(entry.getKey());
            }
        }
    }
}

Some additional info... May be useful to you

Above method may not be good if your hashmap is really big. If your hashmap contain unique key to unique value mapping, you can maintain one more hashmap that contain mapping from Value to Key.

That is you have to maintain two hashmaps

1. Key to value

2. Value to key 

In that case you can use second hashmap to get key.

Solution 4

You could insert both the key,value pair and its inverse into your map structure

map.put("theKey", "theValue");
map.put("theValue", "theKey");

Using map.get("theValue") will then return "theKey".

It's a quick and dirty way that I've made constant maps, which will only work for a select few datasets:

  • Contains only 1 to 1 pairs
  • Set of values is disjoint from the set of keys (1->2, 2->3 breaks it)

Solution 5

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.
Share:
1,047,150
Nick Heiner
Author by

Nick Heiner

JS enthusiast by day, horse mask enthusiast by night. Talks I've Done

Updated on December 29, 2021

Comments

  • Nick Heiner
    Nick Heiner over 2 years

    If I have the value "foo", and a HashMap<String> 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?

  • Vineet Reynolds
    Vineet Reynolds almost 15 years
    Clever, but what if there are 2 or more KeyValue objects containing the same value? Which key should one choose?
  • Esko
    Esko almost 15 years
    ...and if you like Generics and all that modern stuff, Google Collections has BiMap where you can get key matching specified value by calling biMap.inverse().get(value);
  • Vineet Reynolds
    Vineet Reynolds almost 15 years
    Yes, Apache Commons Collections doesn't support generics. However, there is Google Collections as you've pointed out (which I don't use yet - no 1.0 release yet), and there is the refactored Commons-Collections with support for Generics. You'll find this as a Sourceforge project @ sourceforge.net/projects/collections
  • CPerkins
    CPerkins almost 15 years
    Yes, that's exactly what it does. But of course it returns true as soon as it finds one value for which .equals is true, as opposed to what OP will probably need to do.
  • Jonas K
    Jonas K almost 15 years
    Well, iterating over entries can return with key as soon as it finds a matching value too. Multiple matches did not seem to be a concern.
  • whiskeysierra
    whiskeysierra almost 14 years
    The Google Collections are not a refactored version of Commons-Collections.
  • Qiang Li
    Qiang Li almost 13 years
    @Vineet, I don't see how this approach solves the OP's question. what did you mean by "Then when you have a value, you also have the key."?
  • huff
    huff almost 13 years
    @whiskeysierra: I don't think anyone is (currently) saying so.
  • Jonathan
    Jonathan over 11 years
    I think this answer could be improved by adding an explanation.
  • jlordo
    jlordo over 11 years
    -1. I tested it with String as key and value. When I call map.add("1", "2"); map.add("1","3"); then I can call map.getKey("2"); and retrieve "1", even though "1" is the key for "3".
  • tasomaniac
    tasomaniac almost 11 years
    Can you say anything about the performance? What will be more optimized? This or BidiMap?
  • arjacsoh
    arjacsoh almost 11 years
    I have thought the same solution, I have upvoted it of course but I doubt on its efficiency when it comes to really large Collections.
  • Ogre Psalm33
    Ogre Psalm33 over 10 years
    It looks like Apache Commons will get a BidiMap with generics support in release 4.0 (currently in alpha release): commons.apache.org/proper/commons-collections/javadocs/…
  • Chicowitz
    Chicowitz over 9 years
    @Jonathan the idea behind this class is to keep another HashMap with the reverse mappings so that in addition to retrieving a value from a key, you can retrieve a key from a value. The T1 & T2 classes are a bit confusing; maybe literally name them Key & Value instead? Although I would expect the ability to receive more than one value or more than one key in return, depending on the data & what you want. Use with caution
  • Price
    Price over 9 years
    Is having two separate lists for keys and values a bad way to solve this problem? If you know the index of a key in the list of keys, you can get the value from that same index in the values list.
  • jlordo
    jlordo over 9 years
    @theknightwhosaysni "1" is not the key for "2" (anymore). This is also the answer to your question, calling getValue("1") will return 3.
  • Chicowitz
    Chicowitz over 9 years
    Sorry jlordo, I was mistaken about standard Hashmap behavior: you are correct in that adding a new value for a key should replace the old value
  • Joehot200
    Joehot200 over 9 years
    This solution is horribly intensive, to the point where it is impractical on large HashMaps.
  • veer7
    veer7 about 9 years
    stackoverflow.com/questions/4553624/hashmap-get-put-complexi‌​ty HashMap has time complexity o(1). If you are iterating over the values then it will kill the performance. If you want a better performance and has a one-one relationship, you can use another map where value is a key
  • Holger
    Holger about 9 years
    I recommend to replace .filter(entry -> entry.getValue().equals(value)) with .filter(entry ->Objects.equals(entry.getValue(), value)) as no statement about nullability was made. Further, you can replace .map(entry -> entry.getKey()) with .map(Map.Entry::getKey)
  • Viktor Vix Jančík
    Viktor Vix Jančík about 9 years
    Apache Collections now supports generics commons.apache.org/proper/commons-collections/javadocs/…
  • Luis A. Florit
    Luis A. Florit over 8 years
    This is not really correct. This does not only requires 1-1, but also that the set of values is disjoint from the set of keys. You can't apply this to the bijective map {1 -> 2, 2 -> 3}: 2 is both a value and a key.
  • Tom
    Tom almost 8 years
    And why should someone use that approach? As you already said, the performance would be worse, than in other approaches.
  • Fran Marzoa
    Fran Marzoa over 7 years
    I think this is an interesting approach, though since the relation has to be 1:1, I'd get rid of HashMap altogether and implement Map<K,V> interface instead so to avoid duplicates of both, values and keys.
  • Tom
    Tom over 7 years
    It is nice that you want to provide useful stuff, but it shouldn't be a "code only" answer and the code itself shouldn't be full of code smells, either.
  • ponderingdev
    ponderingdev over 7 years
    i'm having difficulty understanding the <T, E> notation before Set<T> getKeysByValue()...what's the point....different way to do it without using that? thanks
  • Toby Wilson
    Toby Wilson over 7 years
    This solution works for me; also developing for an archaeological Android version, in my case to get the key of a Google Map marker held in a Map in an "onMarkerClick" event. Iterating the entrySet works; but iterating the keys and matching them to entries with get(), and comparing the output, didn't.
  • Aquarius Power
    Aquarius Power over 7 years
    guava's HashBiMap.create()
  • Admin
    Admin about 7 years
    Map<String, Integer> map = new HashMap<String, Integer>(); map.put("A", 1); map.put("B", 2); map.put("C", 3); map.put("D", 4); // System.out.println(map); System.out.println(getKey(map, "4"));
  • Cà phê đen
    Cà phê đen about 7 years
    What happens if multiple keys have the same value?
  • Admin
    Admin about 7 years
    When u pass the multiple keys have the same value, we will get the last key as result. example: A 1, B 1, C 1, D 2 output: if we pass the 1 value , output will be C
  • Niels Doucet
    Niels Doucet almost 7 years
    @AmazingIndia This is not guaranteed and completely depends on the specific map implementation. HashMap for instance does not guarantee an order, so you have no idea what output will be returned here.
  • frododot
    frododot over 5 years
    map.get(key) == value is not a good idea when checking equality of objects, as you are comparing references. Object equality should always use their .equals()
  • frododot
    frododot over 5 years
    @Anton, true unless value has been interned.
  • Aliton Oliveira
    Aliton Oliveira about 4 years
    Call requires API Level 24: java.util.Collection#stream
  • Admin
    Admin over 2 years
    As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.