70

Can you suggest a kind of map or similar data structure where we can get both the value and key from each other at equal ease. That is to say, that each may be used to find other.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
mawia
  • 9,169
  • 14
  • 48
  • 57

9 Answers9

48

Java doesn't have a bidirectional map in its standard library.

Use for example BiMap<K, V> from Google Guava .

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Jesper
  • 202,709
  • 46
  • 318
  • 350
  • @Travis Thanks, links updated. (The API docs are still on Google Code at the moment). – Jesper Apr 22 '15 at 18:49
  • Yeah I noticed that too hopefully they will moved to somewhere safer. – Travis Apr 22 '15 at 18:56
  • @Travis They're both from Google, so they probably won't mess up their own project... – Jesper Apr 22 '15 at 19:22
  • @Jesper - Google probably needs BiMap for specific use cases. But, why does Java not provide BiMap ? Its nice to have options in data structures & algorithms instead of having to get them from libraries or coding from scratch. – MasterJoe Jul 11 '20 at 19:31
  • 1
    @MasterJoe2 There's no good answer to the question "why is it not in the standard library". We can only say: because the people who wrote the standard library didn't add it. – Jesper Jul 13 '20 at 04:53
37

If you feel it pain importing some third party library. How about this simple class.

public class BiMap<K,V> {

    HashMap<K,V> map = new HashMap<K, V>();
    HashMap<V,K> inversedMap = new HashMap<V, K>();

    void put(K k, V v) {
        map.put(k, v);
        inversedMap.put(v, k);
    }

    V get(K k) {
        return map.get(k);
    }

    K getKey(V v) {
        return inversedMap.get(v);
    }

}

Make sure K and V class has proper hashCode implementation.

Rohit Sharma
  • 13,787
  • 8
  • 57
  • 72
  • 5
    The problem is that this is now not a collection, so all the collections methods don't work. – Justin Jul 12 '16 at 19:41
  • 1
    true. i ended up in adding what i need – Rohit Sharma Jul 13 '16 at 06:33
  • 3
    Well, this BiMap method could implement Map right ? Then it becomes a collection. – YetAnotherBot Apr 04 '18 at 10:19
  • 3
    not a clean way imo. it has to be a case of composition not inheritance – Rohit Sharma Apr 05 '18 at 12:56
  • 2
    You can just extend from `HashMap` and keep an inversed map internally. Then override all the methods that does the mutations, and add in the corresponding methods for retrieval via value. – Jai Nov 08 '18 at 08:29
  • 1
    If importing is a pain, consider moving to Maven or Gradle. – Joe Coder Aug 01 '19 at 03:40
  • 2
    There is another issue with this solution. Values are not unique, so if two keys point to the same value and we use getKey() with this value we will always get the second key while the map will keep both keys for this value. – axxis Feb 05 '21 at 10:22
14

The most common solution is using two maps. You can easily encapsulate them in a class with a friendly interface by extending AbstractMap. (Update: This is how Guava's HashBiMap is implemented: two maps)

Creating a new data structure using nothing but arrays and custom classes has few advantages. The map implementations are lightweight wrappers of a data structure that indexes the keys. Since you need two indexes you might as well use two complete maps.

Joni
  • 108,737
  • 14
  • 143
  • 193
11

Also try Apache Commons Collections 4 BidiMap Package.

kervin
  • 11,672
  • 5
  • 42
  • 59
6

Google Guava contains a BiMap (BiDirectional Map).

Pang
  • 9,564
  • 146
  • 81
  • 122
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • You should update the link to point to Github I was going to but thought it might be to trivial since the current link works for now. – Travis Apr 22 '15 at 18:12
4

well for the average usecase where you need a Dictionary like that, I see nothing wrong with a KISS solution, just put'ting the key and value vice versa, saving the overhead of a second Map or even library only for that purpose:

myMap.put("apple", "Apfel");
myMap.put("Apfel", "apple");
Gregor
  • 1,297
  • 1
  • 19
  • 31
  • 5
    Note that this only works if key and value are the same type, and you won't know any more which one is key and which one is value. Which can be fine in some cases. – Philipp Nowak Sep 21 '16 at 11:53
  • @PhilippNowak You could have some way of differentiating the "keys" from the "values" whether by a specific range of values or by storing two sub-type in the HashMap. (Like if your domain has only positive number as its range, you could make the keys be negative values, though the best would be some sort of Binary Enum like in Rust to easily define a data structure like that). – Raphaël Duchaîne Sep 26 '22 at 02:05
3

Based on this answer in this QA and its comments I wrote following. [Will be tested]

Bidirectional Map

import java.util.HashMap;

public class BidirectionalMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
public HashMap<V, K> inversedMap = new HashMap<V, K>();

public K getKey(V value) {              
    return inversedMap.get(value);
}

@Override
public int size() {
    return this.size();
}

@Override
public boolean isEmpty() {
    return this.size() > 0;
}

@Override
public V remove(Object key) {
    V val=super.remove(key);
    inversedMap.remove(val);
    return val;
}

@Override
public V get(Object key) {
    return super.get(key);
}

@Override
public V put(K key, V value) {      
    inversedMap.put(value, key);
    return super.put(key, value);
}

}
Davut Gürbüz
  • 5,526
  • 4
  • 47
  • 83
0

You can define an enum and define helper method to get key. Performance is way too far better compared to BidiMap. E.g

public enum Fruit {
        APPLE("_apple");
        private final String value;
        Fruit(String value){
            this.value=value;
        }
        public String getValue(){
            return this.value;
        }
        public static String getKey(String value){
            Fruit fruits[] = Fruit.values();
            for(Fruit fruit : fruits){
                if(value.equals(fruit.value)){
                    return fruit.name();
                }
            }
            return null;        }
    }
  • Why should this performance be better than having a Map-structure with O(1). Your getKey-method iterates through all elements.. – nimo23 Jul 07 '23 at 12:21
0

Based on this tutorial I suggest the following as answer:

public class IdToNames {
  public static void main(String[] args){
    BidiMap<String, Integer> map = new DualHashBidiMap<>();

    map.put("NameA", 100);
    map.put("NameB", 200);

    System.out.println(map.size()); //2 as expected
    System.out.println(map.get("NameA")); //100 as expected
    System.out.println(map.getKey(100)); //"NameA" as expected
  }
}

Note the problem of duplicated keys and/or values described in this question here

Simeon
  • 748
  • 1
  • 9
  • 26