Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bidirectional multi-valued map in Java

Tags:

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.

like image 527
Bojana Popovska Avatar asked Nov 09 '11 14:11

Bojana Popovska


People also ask

Is hashmap bidirectional?

It's a hash table with two lookup methods: one that gives you a value when you pass a key (like a normal hash table) and one that gives you a key when you pass a value. You can go both ways, hence, bidirectional.

What is Bimap Java?

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.

What is BidiMap?

public interface BidiMap<K,V> extends IterableMap<K,V> 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.

What is multi map in Java?

A Multimap is a new collection type that is found in Google's Guava library for Java. A Multimap can store more than one value against a key. Both the keys and the values are stored in a collection, and considered to be alternates for Map<K, List<V>> or Map<K, Set<V>> (standard JDK Collections Framework).


1 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:

  1. If your "BiMultimap" is going to immutable constant - use Multimaps.invertFrom and ImmutableMultimap / ImmutableListMultimap / ImmutableSetMultimap (each of theese three has different collection storing values). Some code (example taken from app I develop, uses Enums and Sets.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));     } } 
  2. 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 call invertFrom each time you want to have its inverted copy (and making that copy unmodifiable to make sure that you accidentally don't modify vToKMultimap what wouldn't update kToVMultimap). This is not optimal but should do in this case.

  3. (Not your case probably, mentioned as bonus): BiMap interface and implementing classes has .inverse() method which gives BiMap<V, K> view from BiMap<K, V> and itself after biMap.inverse().inverse(). If this issue I mentioned before is done, it will probably have something similar.

  4. (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
like image 134
Xaerxess Avatar answered Oct 11 '22 20:10

Xaerxess