185 votes

Comment implémenter une Map avec des clés multiples ?

J'ai besoin d'une structure de données qui se comporte comme une Map, mais qui utilise plusieurs clés (de types différents) pour accéder à ses valeurs.
(Ne soyons pas trop général, disons que deux clés)

Les clés sont garanties comme étant uniques.

Quelque chose comme :

MyMap<K1,K2,V> ...

Avec des méthodes comme :

getByKey1(K1 key)...
getByKey2(K2 key)...
containsKey1(K1 key)...
containsKey2(K2 key)...

Avez-vous des suggestions ?

La seule chose à laquelle je peux penser est :
Écrivez une classe qui utilise deux cartes en interne.

EDIT Certaines personnes me suggèrent d'utiliser un tuple , a paire ou similaire comme clé pour Map de Java, mais cette ne fonctionnerait pas pour moi :
Je dois être en mesure, comme écrit ci-dessus, de rechercher des valeurs par une seule des deux clés spécifiées.
Les cartes utilisent des codes de hachage des clés et vérifient leur égalité.

0 votes

Je suis stupéfait que cette question n'ait pas été améliorée malgré près de 200 000 vues.

112voto

Jeremy Huiskamp Points 3103

Deux cartes. Un site Map<K1, V> et un Map<K2, V> . Si vous devez avoir une seule interface, écrivez une classe wrapper qui implémente lesdites méthodes.

7 votes

Er, oui, c'est exactement ce que vous avez proposé. Deux Maps, c'est ce qu'il faut faire. Pour étendre à un nombre arbitraire de clés, ayez une méthode comme getByKey(MetaKey mk, Object k) et ensuite une Map<MetaKey, Map<Object, V>> en interne.

0 votes

Toujours emballer les deux cartes dans une classe !

45voto

Nathan Feger Points 7675

Commons-collections fournit exactement ce que vous recherchez : https://commons.apache.org/proper/commons-collections/apidocs/

On dirait que maintenant les commons-collections sont tapées.

Une version dactylographiée peut être trouvée à l'adresse suivante https://github.com/megamattron/collections-generic

Cela correspond exactement à votre cas d'utilisation :

 MultiKeyMap<k1,k2,...,kn,v> multiMap = ??

1 votes

Je pense que l'utilisation des génériques (avec la librairie alternate collections) n'est possible que si toutes vos clés sont du même type. MultiKeyMap<k1,k2,v> ne compile tout simplement pas.

83 votes

Je pense que cette réponse est fausse. Il n'y a aucun moyen d'obtenir une valeur de Commons MultiKeyMap en utilisant simplement une deuxième clé. Vous devez toujours spécifier à la fois key1 et key2. Pourquoi cette réponse a-t-elle obtenu autant de votes positifs ?

1 votes

@Zombies, vous n'avez pas compris mon point de vue. Il n'y a aucun moyen de résoudre le problème initial avec la classe MultiKeyMap de Commons-collections. Si c'est le cas, expliquez-le, s'il vous plaît.

44voto

Logan Capaldo Points 22145

Je continue à suggérer la solution des 2 cartes, mais avec une petite modification.

Map<K2, K1> m2;
Map<K1, V>  m1;

Ce système vous permet de disposer d'un nombre arbitraire d'"alias" de clés.

Il vous permet également de mettre à jour la valeur par le biais de n'importe quelle clé sans que les cartes ne soient désynchronisées.

20 votes

Le seul problème est que vous n'avez pas de K1.

1 votes

Milhous : Oui, c'est un problème avec cette solution.

0 votes

La mise à jour est un problème dans ma réponse également. Par exemple, vous stockez la valeur "1" avec K1=A et K2=B, puis vous stockez la valeur "1" avec K1=C et K2=D. Maintenant vous essayez de définir la valeur = "2" où K1=A. Comment savez-vous quel K2 utiliser ? Logan, votre solution fonctionne, mais vous pourriez également vouloir conserver une Map<K1, K2>. Cela devient probablement un peu théorique cependant, car il ne semble pas que la question originale prenne en compte les mises à jour.

40voto

Tombart Points 4503

Une autre solution consiste à utiliser La goyave de Google

import com.google.common.collect.Table;
import com.google.common.collect.HashBasedTable;

Table<String, String, Integer> table = HashBasedTable.create();

L'utilisation est très simple :

String row = "a";
String column = "b";
int value = 1;

if (!table.contains(row, column)) {
    table.put(row, column, value);
}

System.out.println("value = " + table.get(row, column));

La méthode HashBasedTable.create() fait essentiellement quelque chose comme ça :

Table<String, String, Integer> table = Tables.newCustomTable(
        Maps.<String, Map<String, Integer>>newHashMap(),
        new Supplier<Map<String, Integer>>() {
    public Map<String, Integer> get() {
        return Maps.newHashMap();
    }
});

si vous essayez de créer des cartes personnalisées, vous devriez opter pour la deuxième option (comme le suggère @Karatheodory), sinon la première devrait vous convenir.

2 votes

Dans l'exemple ci-dessus, il serait préférable d'utiliser la fabrique par défaut de Guava pour les tables basées sur le hachage : HashBasedTable.create() . Le site newCustomTable() ne doit être utilisée que pour les cartes réellement personnalisées.

21voto

Guigon Points 21

Et si vous déclariez la classe "Clé" suivante :

public class Key {
   public Object key1, key2, ..., keyN;

   public Key(Object key1, Object key2, ..., Object keyN) {
      this.key1 = key1;
      this.key2 = key2;
      ...
      this.keyN = keyN;
   }

   @Override   
   public boolean equals(Object obj) {
      if (!(obj instanceof Key))
        return false;
      Key ref = (Key) obj;
      return this.key1.equals(ref.key1) && 
          this.key2.equals(ref.key2) &&
          ...
          this.keyN.equals(ref.keyN)
   }

    @Override
    public int hashCode() {
        return key1.hashCode() ^ key2.hashCode() ^ 
            ... ^ keyN.hashCode();
    }

}

Déclarer la carte

Map<Key, Double> map = new HashMap<Key,Double>();

Déclaration de l'objet clé

Key key = new Key(key1, key2, ..., keyN)

Remplir la carte

map.put(key, new Double(0))

Récupérer l'objet sur la carte

Double result = map.get(key);

1 votes

Cette approche est plus puissante qu'il n'y paraît à première vue. vous pouvez même simplement utiliser une Object[] comme clé

0 votes

Les arguments de l'ellipse sont-ils de véritables ellipses ? Ou bien vous indiquez simplement que l'utilisateur doit spécifier 1, 2 ou jusqu'à N lorsqu'il crée la classe. En d'autres termes, ce n'est pas une classe qui va gérer un nombre variable de clés, n'est-ce pas ?

3 votes

Le PO ne veut pas combiner les clés, mais les utiliser séparément.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X