views:

192

answers:

6

Hi,

I have to classes, ClassA and ClassB and a "many to many" AssociationClass. I want to use a structure to hold the associations between A and B such as I can know, for each instance of A or B, which are their counterparts.

I thought of using a Hashmap, with pair keys:

Hasmap<Pair<ClassA, ClassB>, AssociationClass> associations;

This way, I can add and remove an association between two instances of ClassA and ClassB, and I can query a relation for two given instances.

However, I miss the feature of getting all associations defined for a given instance of ClassA or ClassB.

I could do it by brute force and loop over all keys of the map to search for associations between a given instance, but this is inefficient and not elegant.

Do you know of any data structure / free library that enables this ? I don't want to reinvent the wheel.

Thanks in advance for your help,

Raphael

NB: This is not a "database" question. These objects are pure POJO used for live computation, I don't need persistence stuff.

A: 

Using your AssociationClass, you could just have ClassA and ClassB both contain a reference to AssociationClass:

private AssociationClass association;

Or, a different method...

ClassA can contain:

private List<ClassB> classBList;

and ClassB can contain:

private List<ClassA> classAList;

By implementing this, you can access your associations from within the associated class.

Kevin Crowell
Yes, but I would need to keep them synchronized somehow. I think I prefer to have an independant association holder that does the job separately.
Raphael Jolivet
It would be easy to keep them syncronized. You can write in that logic to your setter Properties.
Kevin Crowell
+1  A: 

Maybe the Multimap or the BiMap from the Google Collections Library can do what you need.

jqno
I've looked it.This won't help in this case, but I keep this in my fav's.I didn't know this library. This might be helpful.Thanks.
Raphael Jolivet
+1  A: 

This looks like a problem in which you have data that you want to get using multiple keys. You want to search by ClassA and also by ClassB. This usually leads to multiple maps atop the data so that each map keeps a search key into the underlying data. Perhaps something like this would work:

public class Data {

  public ClassA a;
  public ClassB b;
  public AssociationClass association;

}

Map<ClassA, Data> aData;
Map<ClassB, Data> bData;
Map<AssociationClass, Data> associationData;

Inserting goes like this:

Data data = new Data()

aData.put(data.a, data);
bData.put(data.b, data);
associationData.put(data.association, data);

Getting the data you can query each of the maps to get what you want. You can even have your Pair class as another index into the data:

Map<Pair<ClassA, ClassB>, Data> pairData;

The problem with this approach is that if the underlying data changes a lot you must make sure that all maps are in sync. If this is mostly a readonly problem then you create the maps and then just query the one with your key into the data.

rmarimon
Yes, I definitely need several maps.But I think I need two map of maps.
Raphael Jolivet
A: 

rmarimon is right that it requires two maps, but I think you want A-B, not A-data and B-data.

So you just simply need two maps:


    Hashmap bByA = new HashMap();
    Hashmap aByB = new HashMap();

This gives you everything you seem to want, free and easy.

CPerkins
A: 

Why not put a map in each class?

class ClassA {
    ...
    private Map<ClassB, AssociationClass> associations
            = HashMap<ClassB, AssociationClass>();
    ...
}

class ClassA {
    ...
    private Map<ClassA, AssociationClass> associations
            = HashMap<ClassB, AssociationClass>();
    ...
}
Maurice Perry
A: 

Hi,

Thanks for your suggestions.

I finally reinvented the wheel ... I have written a generic class for holding associations. I use two maps of maps, synchronized.

The associations holder provides the following methods

void setAssociation(LeftClass left, RightClass right, AssociationClass assoc);
AssociationClass getAssociation(LeftClass left, RightClass right);
Map<RightClass, AssociationClass> getAssocationsLeft(LeftClass left);
Map<LeftClass, AssociationClass> getAssocationsRight(RightClass right); 
void removeAssociation(LeftClass left, RightClass right);

Here is the code:

import java.util.HashMap;

/** This class holds many to many associations between two classes. */
public class AssociationHolder<LeftClass, RightClass, AssociationClass> {

    // -------------------------------------------------------
    // Attributes
    // -------------------------------------------------------

    private HashMap<LeftClass, HashMap<RightClass, AssociationClass>> associationsLeft = 
        new HashMap<LeftClass, HashMap<RightClass,AssociationClass>>();
    private HashMap<RightClass, HashMap<LeftClass, AssociationClass>> associationsRight = 
        new HashMap<RightClass, HashMap<LeftClass,AssociationClass>>();     

    // -------------------------------------------------------
    // Methods
    // -------------------------------------------------------

    /** 
     *  Set an association between two instance.
     *  Any prior association is overwritten.
     */
    public void setAssociation(LeftClass left, RightClass right, AssociationClass association) {

        // Get the map for the left 
        HashMap<RightClass, AssociationClass> leftMap = this.associationsLeft.get(left);

        // No association defined yet for this left key ? => Create new map
        if (leftMap == null) {
            leftMap = new HashMap<RightClass, AssociationClass>();
            this.associationsLeft.put(left, leftMap);
        }

        // Get the map for the right 
        HashMap<LeftClass, AssociationClass> rightMap = this.associationsRight.get(right);

        // No association defined yet for this right key ? => Create new map
        if (rightMap == null) {
            rightMap = new HashMap<LeftClass, AssociationClass>();
            this.associationsRight.put(right, rightMap);
        }

        // Set the assoication on both maps
        leftMap.put(right, association);
        rightMap.put(left, association);        

    } 

    /** @return null if no association found. */
    public AssociationClass getAssociation(LeftClass left, RightClass right) {

        // Use left maps (could have used the right one as well)
        HashMap<RightClass, AssociationClass> leftMap = this.associationsLeft.get(left);
        if (leftMap == null) return null;
        return leftMap.get(right);
    }

    /** Get all associations defined for a given Left instance.  */
    public HashMap<RightClass, AssociationClass> getAssociationsLeft(LeftClass left) {

        HashMap<RightClass, AssociationClass> leftMap = this.associationsLeft.get(left);

        // No map defined ? return empty one instead of null
        if (leftMap == null) {
            return new HashMap<RightClass, AssociationClass>();
        } else {
            return leftMap;
        }   
    }

    /** Get all associations defined for a given Right instance.  */
    public HashMap<LeftClass, AssociationClass> getAssociationsRight(RightClass right) {

        HashMap<LeftClass, AssociationClass> rightMap = this.associationsRight.get(right);

        // No map defined ? return empty one instead of null
        if (rightMap == null) {
            return new HashMap<LeftClass, AssociationClass>();
        } else {
            return rightMap;
        }   
    }

    /** 
     *  Remove an association between two instances.
     */
    public void removeAssociation(LeftClass left, RightClass right) {
        HashMap<RightClass, AssociationClass> leftMap = this.getAssociationsLeft(left);
        HashMap<LeftClass, AssociationClass> rightMap = this.getAssociationsRight(right);
        leftMap.remove(right);      
        rightMap.remove(left);  
    }
}

I hope this can help someone in the future.

Raphael Jolivet