web-dev-qa-db-fra.com

Carte inversée Java

J'ai besoin de créer une carte inverse - sélectionnez des valeurs uniques et recherchez des clés. Il semblerait que le seul moyen consiste à itérer toutes les paires clé/valeur, car entrySet renvoie un ensemble de valeurs non unique?.

24
user710818

Les valeurs d'une carte peuvent ne pas être uniques. Mais s’ils le sont (dans votre cas), vous pouvez faire ce que vous avez écrit dans votre question et créer une méthode générique pour la convertir:

private static <V, K> Map<V, K> invert(Map<K, V> map) {

    Map<V, K> inv = new HashMap<V, K>();

    for (Entry<K, V> entry : map.entrySet())
        inv.put(entry.getValue(), entry.getKey());

    return inv;
}

Java 8:

public static <V, K> Map<V, K> invert(Map<K, V> map) {
    return map.entrySet()
              .stream()
              .collect(Collectors.toMap(Entry::getValue, Entry::getKey));
}

Exemple d'utilisation:

public static void main(String[] args) {

    Map<String, Integer> map = new HashMap<String, Integer>();

    map.put("Hello", 0);
    map.put("World!", 1);

    Map<Integer, String> inv = invert(map);

    System.out.println(inv); // outputs something like "{0=Hello, 1=World!}"
}

Note latérale: la méthode put(.., ..) renverra "l'ancienne" valeur d'une clé. Si ce n'est pas nul, vous pouvez lancer une new IllegalArgumentException("Map values must be unique") ou quelque chose comme ça. 

33
dacwe

Jetez un coup d'oeil à Google Guava BiMap .

Exemple d'utilisation

Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = HashBiMap.create(map).inverse();
9
Rhangaun

Pour obtenir une forme inversée d'une carte donnée dans Java 8:

public static <K, V> Map<V, K> inverseMap(Map<K, V> sourceMap) {
    return sourceMap.entrySet().stream().collect(
        Collectors.toMap(Entry::getValue, Entry::getKey,
           (a, b) -> a) //if sourceMap has duplicate values, keep only first
        );
}

Exemple d'utilisation

Map<Integer, String> map = new HashMap<Integer, String>();

map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = inverseMap(map);
4
mikeyreilly

Il semble que le seul moyen consiste à itérer toutes les paires clé/valeur, car entrySet renvoie un ensemble de la valeur ainsi non unique?

C'est une façon au moins. Voici un exemple:

Map<Integer, String> map = new HashMap<Integer, String>();

map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = new HashMap<String, Integer>();

for (Integer i : map.keySet())
    inverted.put(map.get(i), i);

Dans le cas de valeurs non uniques, cet algorithme mappera la dernière valeur trouvée sur sa clé. (L'ordre des itérations n'étant pas défini pour la plupart des cartes, il devrait correspondre à n'importe quelle solution.)

Si vous voulez vraiment conserver la première valeur trouvée pour chaque clé, vous pouvez la changer en 

if (!inverted.containsKey(map.get(i)))
    inverted.put(map.get(i), i);
3
aioobe

Je donnerais une autre approche à ce problème en donnant une dimension supplémentaire: valeurs en double dans EntrySet.

public static void main(String[] args) {

    HashMap<Integer, String> s = new HashMap<Integer, String>();
    s.put(1, "Value1");
    s.put(2, "Value2");
    s.put(3, "Value2");
    s.put(4, "Value1");

    /*
     * swap goes here
     */
    HashMap<String,List<Integer>> newMap = new HashMap<String, List<Integer>>();
    for (Map.Entry<Integer, String> en : s.entrySet()) {
        System.out.println(en.getKey() + " " + en.getValue());

        if(newMap.containsKey(en.getValue())){
            newMap.get(en.getValue()).add(en.getKey());
        } else {
            List<Integer> tmpList = new ArrayList<Integer>();
            tmpList.add(en.getKey());
            newMap.put(en.getValue(), tmpList);
        }
    }

    for(Map.Entry<String, List<Integer>> entry: newMap.entrySet()){
        System.out.println(entry.getKey() + " " + entry.getValue());
    }
}

Le résultat sera que:

1 valeur1 
2 Valeur2 
3 valeur2 
4 valeur1 
Valeur1 [1, 4] 
Valeur2 [2, 3]

2
Oriol Biosca

Apache Commons Collections fournit également une interface BidiMap pour les cartes bidirectionnelles, ainsi que plusieurs implémentations.

BidiMap JavaDoc

1
Agustí Sánchez

Vous devez supposer que les valeurs peuvent être identiques, puisque le contrat de carte le permet.

À mon avis, la meilleure solution consiste à utiliser un wrapper. Il contiendra la valeur d'origine et ajoutera un identifiant. Sa fonction hashCode () s'appuiera sur l'ID et vous fournirez un accesseur pour la valeur d'origine. Le code ressemblerait à ceci:

public class MapKey
{
    /**
     * A new ID to differentiate equal values 
     */
    private int _id;
    /**
     * The original value now used as key
     */
    private String _originalValue;

    public MapKey(String originalValue)
    {
        _originalValue = originalValue;
       //assuming some method for generating ids...
        _id = getNextId();
    }

    public String getOriginalValue()
    {
        return _originalValue;
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + _id;
        return result;
    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MapKey other = (MapKey) obj;
        if (_id != other._id)
            return false;
        return true;
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("MapKey value is ");
        sb.append(_originalValue);
        sb.append(" with ID number ");
        sb.append(_id);
        return sb.toString();
    }

Inverser la carte ressemblerait à ceci:

public Map <MapKey, Integer> invertMap(Map <Integer, String> map)
{

     Map <MapKey, Integer> invertedMap = new HashMap <MapKey, Integer>();

   Iterator<Entry<Integer, String>> it = map.entrySet().iterator();

   while(it.hasNext())
   {
       //getting the old values (to be reversed)
       Entry<Integer, String> entry = it.next();
       Integer oldKey = entry.getKey();
       String oldValue = entry.getValue();

       //creating the new MapKey
       MapKey newMapKey = new MapKey(oldValue);
       invertedMap.put(newMapKey, oldKey);
   }

   return invertedMap;
}

Imprimer les valeurs quelque chose comme ceci:

for(MapKey key : invertedMap.keySet())
       {
           System.out.println(key.toString() + " has a new value of " +  invertedMap.get(key));

       }

Aucun de ce code n'a été testé, mais je pense que c'est la meilleure solution car il utilise la conception d'héritage OO au lieu de la vérification de style "c" et vous permet d'afficher toutes les clés et valeurs d'origine.

0
toto220

Avec goyave

Multimaps.transformValues(Multimaps.index(map.entrySet(), Map.Entry::getValue),
        Map.Entry::getKey)

Vous obtiendrez une carte multiple (essentiellement une carte de listes) en retour.

0
Kaerber