views:

98

answers:

5

I have a list of objects. The objects are given ID and stored in the Hashtable. If I need a object with particular ID, I simply say

ht.get(ID);

However, sometimes, I need to get ID for given object

ht.get(Object);

The first idea is to use two different HashTables one for ID -> Object mapping and the order for Object -> ID mapping.

Does this sound good enough solution?

+2  A: 

If using an external library is OK, you should check BiMap on google collections: http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/BiMap.html

bashflyng
not using external libs.. am I doomed?
You can always implement it yourself. The most common approach is using 2 hashtables and keeping them in sync. You can get some ideas looking at the source of AbstractBiMap in google-collections.
bashflyng
A: 

What you are looking for is a bidirectional map. You can find it in the commons collections in the classes implementing the BidiMap interface or the Google Guava.

Daff
A: 

What you are looking for is a Bi-directional Map. Try Apache Collections BidiMap.

http://commons.apache.org/collections/api-3.1/org/apache/commons/collections/BidiMap.html

tim_wonil
Ooh, got beaten by 2 seconds. :-)
tim_wonil
A: 

Not that I know of immediatley but you can build one ... How about having a single collection of your objects and several lookup structures (hashmaps or trees) that don't store the objects themselves (for memory saving reasons) but the index into your single collection? This way you use the appropriate lookup structure you need (Id -> object or vice versa) get back an integer value that you can index into your original collection. This way you can do more than a bidirectional lookup in case you need to do so in the future.

djunforgetable
+5  A: 

If you cannot use external collections (as you seem to not want to use given one of your comments) you could write a simple class to do what you want (which, yes, is essentially your first thought), along the lines of (I didn't compile this, and it is just a first thought so could be a bad idea, etc ...):

EDIT: now there are two versions, one that allows for duplicate values and one that does not. The ones that does not will remove the key if the value is overwritten.

This version does not allow duplicate values:

class Foo<K, V>
{
    private final Map<K, V> keyValue;
    private final Map<V, K> valueKey;

    {
        keyValue = new HashMap<K, V>();
        valueKey = new HashMap<V, K>();
    }

    // this makes sure that if you do not have duplicate values.
    public void put(final K key, final V value)
    {
        if(keyValue.containsValue(value))
        {
            keyValue.remove(valueKey.get(value));
        }

        keyValue.put(key, value);
        valueKey.put(value, key);
    }

    public V getValueForKey(final K key)
    {
        return (keyValue.get(key));
    }

    public K getKeyForValue(final V value)
    {
        return (valueKey.get(value));
    }

    public static void main(final String[] argv)
    {
        Foo<String, String> foo;

        foo = new Foo<String, String>();
        foo.put("a", "Hello");
        foo.put("b", "World");
        foo.put("c", "Hello");

        System.out.println(foo.getValueForKey("a"));
        System.out.println(foo.getValueForKey("b"));
        System.out.println(foo.getValueForKey("c"));

        System.out.println(foo.getKeyForValue("Hello"));
        System.out.println(foo.getKeyForValue("World"));
    }
}

This version allows duplicated values and gives you back a list of all of the keys that have a given value:

class Foo<K, V>
{
    private final Map<K, V> keyValue;
    private final Map<V, List<K>> valueKeys;

    {
        keyValue = new HashMap<K, V>();
        valueKeys = new HashMap<V, List<K>>();
    }

    public void put(final K key, final V value)
    {
        List<K> values;

        keyValue.put(key, value);

        values = valueKeys.get(value);

        if(values == null)
        {
            values = new ArrayList<K>();
            valueKeys.put(value, values);
        }

        values.add(key);
    }

    public V getValueForKey(final K key)
    {
        return (keyValue.get(key));
    }

    public List<K> getKeyForValue(final V value)
    {
        return (valueKeys.get(value));
    }

    public static void main(final String[] argv)
    {
        Foo<String, String> foo;

        foo = new Foo<String, String>();
        foo.put("a", "Hello");
        foo.put("b", "World");
        foo.put("c", "Hello");

        System.out.println(foo.getValueForKey("a"));
        System.out.println(foo.getValueForKey("b"));
        System.out.println(foo.getValueForKey("c"));

        System.out.println(foo.getKeyForValue("Hello"));
        System.out.println(foo.getKeyForValue("World"));
    }
}

Hiding the two maps in a class is a good idea, because of you find a better way later all you need to do is replace the innards of the class and the rest of your code is left untouched.

TofuBeer
Perfect answer. For people who are somewhat new to Java it's useful to note that collections are little more than indexes--they do not duplicate the information, just contain references to it. This means that this solution does not duplicate any information contained, it simply contains two hash indexes which is the optimal solution for this problem.
Bill K
That doesn't work properly. You should take care that equal values are not inserted to the map. ConsidermyFoo.put("a", "foo");myFoo.put("b", "foo");
COME FROM
I did consider that, but forgot about the fact it would make one of the maps inconsistent with the other. I fixed it (I believe) in two ways, one removes the invalid key the other uses a list to maintain the duplicates.
TofuBeer