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...
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