2010-04-03 12 views
11

tengo a clases, claseA y ClassB y una "muchos a muchos" AssociationClass. Quiero usar una estructura para mantener las asociaciones entre A y B, como puedo saber, para cada instancia de A o B, que son sus contrapartes.Java muchos a muchos mapa asociación

I pensado en utilizar un Hashmap, con las teclas de par:

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

De esta manera, puedo añadir y eliminar una asociación entre dos instancias de ClassA y ClassB, y puedo consultar una relación para dos instancias dadas.

Sin embargo, echo de menos la característica de tener todas las asociaciones definidas para una instancia determinada de claseA o ClassB.

Podría hacerlo por fuerza bruta y recorrer todas las teclas del mapa para buscar asociaciones entre una instancia determinada, pero esto es ineficaz y no es elegante.

¿Conoce alguna estructura de datos/biblioteca libre que lo habilite? No quiero reinventar la rueda.

Gracias de antemano por su ayuda,

Raphael

NB: Esta no es una pregunta "base de datos". Estos objetos son POJO puro usado para computación en vivo, no necesito material de persistencia.

+0

Haga casos particulares de Clase A ni ClassB puede tener muchas AssociationClass conectan? – rmarimon

+0

Esa es una buena pregunta: No. En mi caso, hay como máximo una asociación para cada pareja. –

+0

, entonces no es muchos para muchos. – CPerkins

Respuesta

3

Gracias por sus sugerencias.

Finalmente reinventé la rueda ... Escribí una clase genérica para asociaciones. Uso dos mapas de mapas sincronizados.

El titular de asociaciones proporciona los siguientes métodos

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); 

Aquí está el código:

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); 
    } 
} 

Espero que esto pueda ayudar a alguien en el futuro.

+0

Cabe señalar que esta clase no es segura para subprocesos – pulse0ne

0

Usando su AssociationClass, sólo podría tener ClassA y ClassB ambos contienen una referencia a AssociationClass:

private AssociationClass association; 

O, un método diferente ...

claseA puede contener:

private List<ClassB> classBList; 

y ClassB puede contener:

private List<ClassA> classAList; 

Al implementar esto, puede acceder a sus asociaciones desde dentro de la clase asociada.

+0

Sí, pero necesitaría mantenerlos sincronizados de alguna manera. Creo que prefiero tener un titular independiente de la asociación que haga el trabajo por separado. –

+0

Sería fácil mantenerlos sincronizados. Puede escribir en esa lógica a sus Propiedades setter. –

1

Tal vez el Multimap o el BiMap del Google Collections Library pueden hacer lo que necesita.

+0

Lo he buscado. Esto no ayudará en este caso, pero mantengo esto en mi favor. No conocía esta biblioteca. Esto podría ser útil. Gracias. –

1

Parece un problema en el que tiene datos que desea obtener utilizando varias teclas. Desea buscar por ClassA y también por ClassB. Esto generalmente conduce a múltiples mapas sobre los datos, de modo que cada mapa mantiene una clave de búsqueda en los datos subyacentes. Tal vez algo como esto funcionaría:

public class Data { 

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

} 

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

Inserción es la siguiente:

Data data = new Data() 

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

Obtener los datos que se puede consultar cada uno de los mapas para obtener lo que desea.Puede incluso tener su clase Pair como otro índice en los datos:

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

El problema con este enfoque es que si los datos subyacentes cambia mucho debe asegurarse de que todos los mapas están en sincronía. Si esto es principalmente un problema de solo lectura, entonces usted crea los mapas y luego solo consulta el que tiene su clave en los datos.

+0

Sí, definitivamente necesito varios mapas. Pero creo que necesito dos mapas de mapas. –

0

rmarimon tiene razón en que requiere dos mapas, pero creo que usted quiere A-B, no A-data y B-data.

Así que simplemente necesitan dos mapas:


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

Esto le da todo lo que pareces querer, libre y fácil.

0

¿Por qué no poner un mapa en cada clase?

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

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

Aquí está mi aplicación basada en la guayaba Multimapa:

public class ImmutableBiMultimap<K, V> { 
    private final ImmutableSetMultimap<K, V> kToV; 
    private final ImmutableSetMultimap<V, K> vToK; 

    public ImmutableBiMultimap (SetMultimap<K, V> keyToValueMap) { 
     kToV = ImmutableSetMultimap.copyOf(keyToValueMap); 

     SetMultimap<V, K> valueToKeyMap = HashMultimap.create(); 
     for (Entry<K, V> entry : kToV.entries()) { 
      valueToKeyMap.put(entry.getValue(), entry.getKey()); 
     } 

     vToK = ImmutableSetMultimap.copyOf(valueToKeyMap); 
    } 

    public ImmutableSet<V> getValuesForKey(K key) { 
     return kToV.get(key); 
    } 

    public ImmutableSet<K> getKeysForValue(V value) { 
     return vToK.get(value); 
    } 
} 
Cuestiones relacionadas