64

I need a data structure to store string-int value pairs in an 1:1 relationship, and being able too look up from either way their counterpart.

I wrote a class with a Hashtable and a String array and stored the data 2 times and used the built in functions for lookup.

My question is that is there a nicer way to accomplish this? And by nicer I mean being efficient and not storing the data 2 times, and preferably without writing a ton of code either :P.

sekmet64
  • 1,635
  • 4
  • 19
  • 30

7 Answers7

57

It seems like you may be looking for a bimap.

The Google Collections (now a part of Guava) contains an BiMap interface with a few implementations.

From the BiMap documentation:

A bimap (or "bidirectional map") is a map that preserves the uniqueness of its values as well as that of its keys. This constraint enables bimaps to support an "inverse view", which is another bimap containing the same entries as this bimap but with reversed keys and values.

The BiMap.inverse method appears to return a Map with the values as the keys, and the keys as the values, so that Map can be used to call get on the value and retrieve a key.

In addition the Map returned by inverse is a view of the underlying data, so it does not have to make extra copies of the original data.

From the BiMap.inverse method documentation:

Returns the inverse view of this bimap, which maps each of this bimap's values to its associated key. The two bimaps are backed by the same data; any changes to one will appear in the other.

mjlitz
  • 148
  • 10
coobird
  • 159,216
  • 35
  • 211
  • 226
  • Javadoc link in answer no longer works. Substitute: https://google.github.io/guava/releases/snapshot-jre/api/docs/com/google/common/collect/BiMap.html – Jim Davis Nov 10 '18 at 21:54
  • Like an actual example of how to use that library would be nice. It is just an interface with tons of implementations. – mjs Mar 01 '23 at 08:47
34

You can do a simple implementation like this. Please note that the data is not copied in this implementation. Only the references are ! I have added implementation for add and get. remove and other required method are left as exercise :)

public class TwoWayHashmap<K extends Object, V extends Object> {

  private Map<K,V> forward = new Hashtable<K, V>();
  private Map<V,K> backward = new Hashtable<V, K>();

  public synchronized void add(K key, V value) {
    forward.put(key, value);
    backward.put(value, key);
  }

  public synchronized V getForward(K key) {
    return forward.get(key);
  }

  public synchronized K getBackward(V key) {
    return backward.get(key);
  }
}

And ofcourse its applications responsibility to ensue even the 'values' are unique. Example usage:

TwoWayHashmap twmap = new TwoWayHashmap<String, String>();
twmap.add("aaa", "bbb");
twmap.add("xxx", "yyy");
System.out.println(twmap.getForward("xxx"));
System.out.println(twmap.getBackward("bbb"));
Gopi
  • 10,073
  • 4
  • 31
  • 45
14

Apache Commons also includes the BidiMap (Bi Directional Map).

Defines a map that allows bidirectional lookup between key and values.

This extended Map represents a mapping where a key may lookup a value and a value may lookup a key with equal ease. This interface extends Map and so may be used anywhere a map is required. The interface provides an inverse map view, enabling full access to both directions of the BidiMap.

jdknight
  • 1,801
  • 32
  • 52
Alex B
  • 24,678
  • 14
  • 64
  • 87
5

Google Guava has a BiMap that does what you want.

jqno
  • 15,133
  • 7
  • 57
  • 84
  • yup that does it! I couldn't find anything by searching for "2 way map" :P it seems bidirectional map is the right term – sekmet64 Aug 07 '10 at 11:13
  • Javadoc link in answer no longer works. Substitute: https://google.github.io/guava/releases/snapshot-jre/api/docs/com/google/common/collect/BiMap.html – Jim Davis Nov 10 '18 at 21:56
4

Using Guava,

    HashBiMap<String, String> map = HashBiMap.create();

    map.put("name", "Sohail");
    map.put("country", "Pakistan");

    Log.d("tag", "name is " + map.get("name"));


    BiMap<String, String>invmap= map.inverse();

    Log.d("tag", "Pakistan is a " + invmap.get("Pakistan"));

read complete tutorial here.

SohailAziz
  • 8,034
  • 6
  • 41
  • 43
1

definitely w/o writing a ton of code → in lambda
from Your Map<String,Integer> map You get the inverted map by

Map<Integer,String> inverted = map.keySet().stream().collect(Collectors.toMap( s -> map.get( s ), s -> s ) );
MiB
  • 575
  • 2
  • 10
  • 26
Kaplan
  • 2,572
  • 13
  • 14
-4

Create a hashmap that maps Object to Object - then you can use the same map to store String -> Integer and Integer -> String.

When you add a string/int pair just add it both ways to the same map.

Dave Kirby
  • 25,806
  • 5
  • 67
  • 84
  • 3
    This is not a very sensible implementation at all. You throw all the generic type safety out the window, and increase the complexity of all the operations carried out on the map by double-adding entries. There is no encapsulation of the mechanism by which you are implementing the bi-directional map. – Tom Aug 07 '10 at 11:27
  • @Tom - I wrote the answer in a hurry and assumed the implementer would be sensible enough to wrap the hashmap in a class that controlled access to it and did the casting to the required types. I will try to be clearer on that point in future. – Dave Kirby Aug 07 '10 at 11:44
  • 1
    Another problem you could run into is storing A->B and B->C, which should be fine with the question but not with your answer. – DerMike Aug 07 '10 at 12:13