3

Is there an existing open source Map implementation for java, which would be a normal key-value map, but would also support multiple values per key? The multimap implementations I've found seem to associate key with collection, which doesn't quite cut it, as I need a drop-in replacement for existing code.

I sense some people saying "you can't do that", so here's an example of one way how it can behave, in a widely used framework, Qt. Here's an excerpt form the docs for QMap class:

If the map contains no item with key key, the function returns a default-constructed value. If there are multiple items for key in the map, the value of the most recently inserted one is returned.

My need is quite limited, so at the moment I'm using the hack below, which is adequate, since there are no removals and many values per key are exception, and the duplicate keys getting a bit mangled is not a problem:

public static <V, V2 extends V> String mapMultiPut(
        Map<String, V> map, 
        String key, 
        V2 value) {
    int count = 0;
    String tmpKey = key;
    while (map.containsKey(tmpKey)) {
        ++count;
        tmpKey = key + '_' + count;
    }
    map.put(tmpKey, value);
    return tmpKey;
}

But I'd like a nicer solution, if one exists...

hyde
  • 60,639
  • 21
  • 115
  • 176
  • 1
    @MarkRotteveel That question does not have the "must be drop-in replacement for normal map" aspect, which is the point of my question. – hyde Nov 15 '12 at 11:39
  • A normal Map.put overwrites the previous value with the new one. What functionality are you missing from that? – Score_Under May 05 '13 at 18:15
  • @Score_Under The question is half a year old, and I don't remember the exact use case, but looking at my old question, the missing functionality is/was equivalent of `QMap::insertMulti`... – hyde May 06 '13 at 14:27

2 Answers2

0

Guava libraries have a Multimap which allows more than one value per key :)

The Cat
  • 2,375
  • 6
  • 25
  • 37
  • As I said in the question: "The multimap implementations I've found seem to associate key with collection, which doesn't quite cut it." – hyde Nov 15 '12 at 11:18
  • Have you read the docs linked by the OP? The expected behavior is for the map to return a single value, inserted most recently, instead of a collection of values. – toniedzwiedz Nov 15 '12 at 11:18
0

You could use a ListMultimap along with

Iterables.getLast(listMultiMap.get(key), defaultValue(key))

where you define your own defaultValue method.

This assumes you don't actually need the Map interface in your class.

If you really want a Map you could try this

public abstract class QtMap<K, V> extends ForwardingMap<K, V>
{
    private final ListMultimap<K, V> listMultimap = ArrayListMultimap.create();

    final Map<K, V> delegate = Maps.<K, Collection<V>, V> transformEntries(listMultimap.asMap(), new EntryTransformer<K, Collection<V>, V>()
    {

        @Override
        public V transformEntry(K key, Collection<V> value)
        {
            return Iterables.getLast(value, defaultValue(key));
        }

    });

    @Override
    protected Map<K, V> delegate()
    {
        return delegate;
    }

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

    @Override
    public void putAll(Map<? extends K, ? extends V> map)
    {
        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet())
        {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public V get(Object key)
    {
        return listMultimap.containsKey(key) ? delegate.get(key) : defaultValue(key);
    }

    protected abstract V defaultValue(Object key);

}

although it's only sketchily tested

artbristol
  • 32,010
  • 5
  • 70
  • 103