I am looking for a way to store key-value pairs. I need the lookup to be bidirectional, but at the same time I need to store multiple values for the same key. In other words, something like a BidiMap, but for every key there can be multiple values. For example, it needs to be able to hold pairs like: "s1"->1, "s2"->1, "s3"->2, and I need to be able to get the value mapped to each key, and for each value, get all the keys associated with it.
-
4You talk about the need of having multiple values per key, but in your example you have no key with multiple values, but one value with two keys. You should probably clarify that. If your example fits to your question you will get better answers ;-) – pushy Nov 09 '11 at 14:08
-
http://www.jguru.com/faq/view.jsp?EID=1317828 here you can find how to create multimap – maks Nov 09 '11 at 14:15
-
@pushy, same problem, if I reverse the map, and keep the integers as keys instead of as values, I get a one-to-many mapping. Anyway, thanks for the correction. :) – Bojana Popovska Nov 09 '11 at 14:31
6 Answers
So you need support for many-to-many relationships? Closest you can get is Guava's Multimap
like @Mechkov wrote - but more specifically Multimap
combination with Multimaps.invertFrom
. "BiMultimap" isn't implemented yet, but there is an issue requesting this feature in Google Guava library.
At this point you have few options:
If your "BiMultimap" is going to immutable constant - use
Multimaps.invertFrom
andImmutableMultimap
/ImmutableListMultimap
/ImmutableSetMultimap
(each of theese three has different collection storing values). Some code (example taken from app I develop, usesEnum
s andSets.immutableEnumSet
):public class RolesAndServicesMapping { private static final ImmutableMultimap<Service, Authority> SERVICES_TO_ROLES_MAPPING = ImmutableMultimap.<Service, Authority>builder() .put(Service.SFP1, Authority.ROLE_PREMIUM) .put(Service.SFP, Authority.ROLE_PREMIUM) .put(Service.SFE, Authority.ROLE_EXTRA) .put(Service.SF, Authority.ROLE_STANDARD) .put(Service.SK, Authority.ROLE_STANDARD) .put(Service.SFP1, Authority.ROLE_ADMIN) .put(Service.ADMIN, Authority.ROLE_ADMIN) .put(Service.NONE, Authority.ROLE_DENY) .build(); // Whole magic is here: private static final ImmutableMultimap<Authority, Service> ROLES_TO_SERVICES_MAPPING = SERVICES_TO_ROLES_MAPPING.inverse(); // before guava-11.0 it was: ImmutableMultimap.copyOf(Multimaps.invertFrom(SERVICES_TO_ROLES_MAPPING, HashMultimap.<Authority, Service>create())); public static ImmutableSet<Authority> getRoles(final Service service) { return Sets.immutableEnumSet(SERVICES_TO_ROLES_MAPPING.get(service)); } public static ImmutableSet<Service> getServices(final Authority role) { return Sets.immutableEnumSet(ROLES_TO_SERVICES_MAPPING.get(role)); } }
If you really want your Multimap to be modifiable, it will be hard to maintain both K->V and V->K variants unless you will be modifying only
kToVMultimap
and callinvertFrom
each time you want to have its inverted copy (and making that copy unmodifiable to make sure that you accidentally don't modifyvToKMultimap
what wouldn't updatekToVMultimap
). This is not optimal but should do in this case.(Not your case probably, mentioned as bonus):
BiMap
interface and implementing classes has.inverse()
method which givesBiMap<V, K>
view fromBiMap<K, V>
and itself afterbiMap.inverse().inverse()
. If this issue I mentioned before is done, it will probably have something similar.(EDIT October 2016) You can also use new graph API which will be present in Guava 20:
As a whole, common.graph supports graphs of the following varieties:
- directed graphs
- undirected graphs
- nodes and/or edges with associated values (weights, labels, etc.)
- graphs that do/don't allow self-loops
- graphs that do/don't allow parallel edges (graphs with parallel edges are sometimes called multigraphs)
- graphs whose nodes/edges are insertion-ordered, sorted, or unordered

- 27,415
- 11
- 90
- 112
I hope using MultivaluedMap solves the problem. Please find the documentation from oracle below link.
http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/MultivaluedMap.html

- 1,997
- 4
- 27
- 39
Using Google Guava we can write a primitive BiMulitMap as below.
import java.util.Collection;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
public class BiMultiMap<K,V> {
Multimap<K, V> keyToValue = ArrayListMultimap.create();
Multimap<V, K> valueToKey = ArrayListMultimap.create();
public void putForce(K key, V value) {
keyToValue.put(key, value);
valueToKey.put(value, key);
}
public void put(K key, V value) {
Collection<V> oldValue = keyToValue.get(key);
if ( oldValue.contains(value) == false ) {
keyToValue.put(key, value);
valueToKey.put(value, key);
}
}
public Collection<V> getValue(K key) {
return keyToValue.get(key);
}
public Collection<K> getKey(V value) {
return valueToKey.get(value);
}
@Override
public String toString() {
return "BiMultiMap [keyToValue=" + keyToValue + ", valueToKey=" + valueToKey + "]";
}
}
Hope this will help some basic needs of the Bi-Directional Multi Map. Note the K and V needs to implement the hascode and equals method properly

- 3,074
- 1
- 25
- 26
What's wrong with having two maps, key->values, values->keys?

- 28,248
- 23
- 84
- 121
-
4I thought that keeping two copies of the same data would be more error prone. Anyway, after all the collections I have looked at, I am starting to think it is the best solution. – Bojana Popovska Nov 09 '11 at 14:32
-
2
-
12I dislike the approach endorsed by this answer. There are many things potentially wrong with this, including possibly reinventing the wheel, writing your own bugs along the way, thread safety, etc. – bacar Apr 20 '12 at 10:30
Hope I got you right
class A {
long id;
List<B> bs;
}
class B {
long id;
List<A> as;
}

- 899
- 8
- 13
Google's Guava MultiMap implementation is what i am using for these purposes.
Map<Key Collection<Values>>
where Collection can be an ArrayList for example. It allows multiple values stored in a collection to be mapped to a key. Hope this helps!

- 4,294
- 1
- 17
- 25