web-dev-qa-db-fra.com

Java Hashmap: Comment obtenir la clé de la valeur?

Si j'ai la valeur "foo" et un HashMap<String> ftw pour lequel ftw.containsValue("foo") renvoie true, comment puis-je obtenir la clé correspondante? Dois-je parcourir le hashmap? Quelle est la meilleure façon de le faire?

399
Nick Heiner

Si vous choisissez d'utiliser la bibliothèque de collections Commons au lieu de l'API Java Collections standard, vous pouvez y parvenir facilement.

L’interface BidiMap de la bibliothèque Collections est une carte bidirectionnelle, qui vous permet de mapper une clé sur une valeur (comme des cartes normales) et également de mapper une valeur sur une clé, vous permettant ainsi d’exécuter recherches dans les deux sens. L'obtention d'une clé pour une valeur est prise en charge par la méthode getKey () .

Il convient toutefois de noter que les mappes bidirectionnelles ne peuvent pas avoir plusieurs valeurs mappées sur des clés. Par conséquent, à moins que votre ensemble de données ne comporte des mappages 1: 1 entre des clés et des valeurs, vous ne pouvez pas utiliser bidimaps.

Mettre à jour

Si vous souhaitez vous fier à l'API Java Collections, vous devez vous assurer que la relation 1: 1 entre les clés et les valeurs est au moment de l'insertion de la valeur dans la carte. C'est plus facile à dire qu'à faire.

Une fois que vous pouvez vous en assurer, utilisez la méthode entrySet () pour obtenir l'ensemble des entrées (mappages) de la mappe. Une fois que vous avez obtenu l'ensemble dont le type est Map.Entry , parcourez les entrées, comparez la valeur stockée à l'attendu et obtenez le clé correspondante .

Mise à jour # 2

Les bibliothèques Google Guava et les bibliothèques refactorisées Commons-Collections (cette dernière n’est pas un projet Apache) sont compatibles avec les génériques. Merci à Esko d’avoir signalé le support générique manquant dans Apache Commons Collections. L'utilisation de collections avec des génériques rend le code plus facile à gérer.

199
Vineet Reynolds

Si votre structure de données dispose d'un mappage plusieurs-à-un entre clés et valeurs, vous devez effectuer une itération sur les entrées et sélectionner toutes les clés appropriées:

public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
    Set<T> keys = new HashSet<T>();
    for (Entry<T, E> entry : map.entrySet()) {
        if (Objects.equals(value, entry.getValue())) {
            keys.add(entry.getKey());
        }
    }
    return keys;
}

Dans le cas d'une relation un-à-un, vous pouvez renvoyer la première clé correspondante:

public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
    for (Entry<T, E> entry : map.entrySet()) {
        if (Objects.equals(value, entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}

En Java 8:

public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
    return map.entrySet()
              .stream()
              .filter(entry -> Objects.equals(entry.getValue(), value))
              .map(Map.Entry::getKey)
              .collect(Collectors.toSet());
}

Aussi, pour les utilisateurs de goyave, BiMap peut être utile. Par exemple:

BiMap<Token, Character> tokenToChar = 
    ImmutableBiMap.of(Token.LEFT_BRACKET, '[', Token.LEFT_PARENTHESIS, '(');
Token token = tokenToChar.inverse().get('(');
Character c = tokenToChar.get(token);
576
public class NewClass1 {

    public static void main(String[] args) {
       Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");
        for (Entry<Integer, String> entry : testMap.entrySet()) {
            if (entry.getValue().equals("c")) {
                System.out.println(entry.getKey());
            }
        }
    }
}

Quelques informations supplémentaires ... Peut vous être utile

La méthode ci-dessus peut ne pas être bonne si votre hashmap est vraiment gros. Si votre hashmap contient un mappage unique de mappage de valeur unique, vous pouvez gérer un mappage de hachage supplémentaire contenant le mappage de Value à Key.

C'est que vous devez maintenir deux hashmaps

1. Key to value

2. Value to key 

Dans ce cas, vous pouvez utiliser la deuxième hashmap pour obtenir la clé.

71
Fathah Rehman P

Vous pouvez insérer la clé, la paire valeur et son inverse dans la structure de votre carte.

map.put("theKey", "theValue");
map.put("theValue", "theKey");

Utiliser map.get ("theValue") retournera alors "theKey".

C'est une façon rapide et sale de créer des cartes constantes, qui ne fonctionneront que pour un petit nombre de jeux de données:

  • Contient seulement 1 à 1 paires
  • L'ensemble de valeurs est disjoint de l'ensemble des clés (1-> 2, 2-> 3 le sépare)
18
Chicowitz

Je pense que vos choix sont

  • Utilisez une implémentation de carte conçue à cet effet, comme le BiMap de Google Collections. Notez que les collections Google BiMap nécessitent des valeurs uniques, ainsi que des clés, mais offrent des performances élevées dans les deux sens
  • Maintenir manuellement deux cartes - une pour clé -> valeur et une autre carte pour valeur -> clé
  • Parcourez le entrySet() et recherchez les clés qui correspondent à la valeur. C'est la méthode la plus lente, car elle nécessite une itération de l'ensemble de la collection, contrairement aux deux autres méthodes.
17
Chi

Décorez la carte avec votre propre implémentation

class MyMap<K,V> extends HashMap<K, V>{

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

    @Override
    public V put(K key, V value) {
        // TODO Auto-generated method stub
        reverseMap.put(value, key);
        return super.put(key, value);
    }

    public K getKey(V value){
        return reverseMap.get(value);
    }
}
11
ABHI

Il n'y a pas de réponse sans équivoque, car plusieurs clés peuvent correspondre à la même valeur. Si vous imposez unique-ness avec votre propre code, la meilleure solution consiste à créer une classe qui utilise deux cartes de hachage pour suivre les mappages dans les deux sens.

11
recursive

Pour rechercher toutes les clés associées à cette valeur, parcourez toutes les paires de la table de hachage à l'aide de map.entrySet().

11
wsorenson

Je pense que c'est la meilleure solution, adresse originale: Java2s

    import Java.util.HashMap;
    import Java.util.Map;

        public class Main {

          public static void main(String[] argv) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("1","one");
            map.put("2","two");
            map.put("3","three");
            map.put("4","four");

            System.out.println(getKeyFromValue(map,"three"));
          }


// hm is the map you are trying to get value from it
          public static Object getKeyFromValue(Map hm, Object value) {
            for (Object o : hm.keySet()) {
              if (hm.get(o).equals(value)) {
                return o;
              }
            }
            return null;
          }
        }

Une utilisation facile: Si vous mettez toutes les données dans hasMap et que vous avez item = "Automobile", vous cherchez donc sa clé dans hashMap. c'est une bonne solution. 

getKeyFromValue(hashMap, item);
System.out.println("getKeyFromValue(hashMap, item): "+getKeyFromValue(hashMap, item));
8
boy

Si vous construisez la carte dans votre propre code, essayez de rassembler la clé et la valeur dans la carte:

public class KeyValue {
    public Object key;
    public Object value;
    public KeyValue(Object key, Object value) { ... }
}

map.put(key, new KeyValue(key, value));

Ensuite, lorsque vous avez une valeur, vous avez également la clé.

7
David Tinker
for(int key: hm.keySet()) {
    if(hm.get(key).equals(value)) {
        System.out.println(key); 
    }
}
6
user309309

Il semble que la meilleure façon de procéder consiste à parcourir les entrées à l'aide de map.entrySet() puisque map.containsValue() le fait probablement de toute façon.

6
Jonas Klemming

J'ai bien peur que vous n'ayez qu'à parcourir votre carte. Le plus court je pourrais venir avec:

Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry<String,String> entry = iter.next();
    if (entry.getValue().equals(value_you_look_for)) {
        String key_you_look_for = entry.getKey();
    }
}
6
André van Toly

Utilisation de Java 8:

ftw.forEach((key, value) -> {
    if (value=="foo") {
        System.out.print(key);
    }
});
5
phani

Pour les API de développement sous Android <19, la solution de relation univoque Vitalii Fedorenko ne fonctionne pas car Objects.equals n’est pas implémenté. Voici une alternative simple:

public <K, V> K getKeyByValue(Map<K, V> map, V value) {
    for (Map.Entry<K, V> entry : map.entrySet()) {
            if (value.equals(entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}
5
The Berga

Vous pouvez utiliser le ci-dessous:

public class HashmapKeyExist {
    public static void main(String[] args) {
        HashMap<String, String> hmap = new HashMap<String, String>();
        hmap.put("1", "Bala");
        hmap.put("2", "Test");

        Boolean cantain = hmap.containsValue("Bala");
        if(hmap.containsKey("2") && hmap.containsValue("Test"))
        {
            System.out.println("Yes");
        }
        if(cantain == true)
        {
            System.out.println("Yes"); 
        }

        Set setkeys = hmap.keySet();
        Iterator it = setkeys.iterator();

        while(it.hasNext())
        {
            String key = (String) it.next();
            if (hmap.get(key).equals("Bala"))
            {
                System.out.println(key);
            }
        }
    }
}
public static String getKey(Map<String, Integer> mapref, String value) {
    String key = "";
    for (Map.Entry<String, Integer> map : mapref.entrySet()) {
        if (map.getValue().toString().equals(value)) {
            key = map.getKey();
        }
    }
    return key;
}
2
Amazing India

Vous pouvez obtenir la clé en utilisant des valeurs en utilisant le code suivant.

ArrayList valuesList = new ArrayList();
Set keySet = initalMap.keySet();
ArrayList keyList = new ArrayList(keySet);

for(int i = 0 ; i < keyList.size() ; i++ ) {
    valuesList.add(initalMap.get(keyList.get(i)));
}

Collections.sort(valuesList);
Map finalMap = new TreeMap();
for(int i = 0 ; i < valuesList.size() ; i++ ) {
    String value = (String) valuesList.get(i);

    for( int j = 0 ; j < keyList.size() ; j++ ) {
        if(initalMap.get(keyList.get(j)).equals(value)) {
            finalMap.put(keyList.get(j),value);
        }   
    }
}
System.out.println("fianl map ---------------------->  " + finalMap);
2
Amit

En java8

map.entrySet().stream().filter(entry -> entry.getValue().equals(value))
    .forEach(entry -> System.out.println(entry.getKey()));
2
user3724331
public static class SmartHashMap <T1 extends Object, T2 extends Object> {
    public HashMap<T1, T2> keyValue;
    public HashMap<T2, T1> valueKey;

    public SmartHashMap(){
        this.keyValue = new HashMap<T1, T2>();
        this.valueKey = new HashMap<T2, T1>();
    }

    public void add(T1 key, T2 value){
        this.keyValue.put(key, value);
        this.valueKey.put(value, key);
    }

    public T2 getValue(T1 key){
        return this.keyValue.get(key);
    }

    public T1 getKey(T2 value){
        return this.valueKey.get(value);
    }

}
2
margus
import Java.util.HashMap;
import Java.util.HashSet;
import Java.util.Set;

public class ValueKeysMap<K, V> extends HashMap <K,V>{
    HashMap<V, Set<K>> ValueKeysMap = new HashMap<V, Set<K>>();

    @Override
    public boolean containsValue(Object value) {
        return ValueKeysMap.containsKey(value);
    }

    @Override
    public V put(K key, V value) {
        if (containsValue(value)) {
            Set<K> keys = ValueKeysMap.get(value);
            keys.add(key);
        } else {
            Set<K> keys = new HashSet<K>();
            keys.add(key);
            ValueKeysMap.put(value, keys);
        }
        return super.put(key, value);
    }

    @Override
    public V remove(Object key) {
        V value = super.remove(key);
        Set<K> keys = ValueKeysMap.get(value);
        keys.remove(key);
        if(keys.size() == 0) {
           ValueKeysMap.remove(value);
        }
        return value;
    }

    public Set<K> getKeys4ThisValue(V value){
        Set<K> keys = ValueKeysMap.get(value);
        return keys;
    }

    public boolean valueContainsThisKey(K key, V value){
        if (containsValue(value)) {
            Set<K> keys = ValueKeysMap.get(value);
            return keys.contains(key);
        }
        return false;
    }

    /*
     * Take care of argument constructor and other api's like putAll
     */
}
1
Kanagavelu Sugumar

Mes 2 centimes Vous pouvez obtenir les clés dans un tableau, puis parcourir le tableau en boucle. Cela affectera les performances de ce bloc de code si la mappe est assez grande et que vous obtenez d'abord les clés dans un tableau qui peut prendre un certain temps, puis que vous effectuez une boucle. Sinon pour les petites cartes ça devrait aller.

String[] keys =  yourMap.keySet().toArray(new String[0]);

for(int i = 0 ; i < keys.length ; i++){
    //This is your key    
    String key = keys[i];

    //This is your value
    yourMap.get(key)            
}
1
Manu Bhat
/**
 * This method gets the Key for the given Value
 * @param paramName
 * @return
 */
private String getKeyForValueFromMap(String paramName) {
    String keyForValue = null;
    if(paramName!=null)) {
        Set<Entry<String,String>> entrySet = myMap().entrySet();
        if(entrySet!=null && entrySet.size>0) {
            for(Entry<String,String> entry : entrySet) {
                if(entry!=null && paramName.equalsIgnoreCase(entry.getValue())) {
                    keyForValue = entry.getKey();
                }
            }
        }
    }
    return keyForValue;
}
1
kanaparthikiran

Utilisez un emballage fin: HMap

import Java.util.Collections;
import Java.util.HashMap;
import Java.util.Map;

public class HMap<K, V> {

   private final Map<K, Map<K, V>> map;

   public HMap() {
      map = new HashMap<K, Map<K, V>>();
   }

   public HMap(final int initialCapacity) {
      map = new HashMap<K, Map<K, V>>(initialCapacity);
   }

   public boolean containsKey(final Object key) {
      return map.containsKey(key);
   }

   public V get(final Object key) {
      final Map<K, V> entry = map.get(key);
      if (entry != null)
         return entry.values().iterator().next();
      return null;
   }

   public K getKey(final Object key) {
      final Map<K, V> entry = map.get(key);
      if (entry != null)
         return entry.keySet().iterator().next();
      return null;
   }

   public V put(final K key, final V value) {
      final Map<K, V> entry = map
            .put(key, Collections.singletonMap(key, value));
      if (entry != null)
         return entry.values().iterator().next();
      return null;
   }
}
1
Jayen
import Java.util.ArrayList;
import Java.util.HashMap;
import Java.util.Iterator;
import Java.util.List;
import Java.util.Set;

public class M{
public static void main(String[] args) {

        HashMap<String, List<String>> resultHashMap = new HashMap<String, List<String>>();

        Set<String> newKeyList = resultHashMap.keySet();


        for (Iterator<String> iterator = originalHashMap.keySet().iterator(); iterator.hasNext();) {
            String hashKey = (String) iterator.next();

            if (!newKeyList.contains(originalHashMap.get(hashKey))) {
                List<String> loArrayList = new ArrayList<String>();
                loArrayList.add(hashKey);
                resultHashMap.put(originalHashMap.get(hashKey), loArrayList);
            } else {
                List<String> loArrayList = resultHashMap.get(originalHashMap
                        .get(hashKey));
                loArrayList.add(hashKey);
                resultHashMap.put(originalHashMap.get(hashKey), loArrayList);
            }
        }

        System.out.println("Original HashMap : " + originalHashMap);
        System.out.println("Result HashMap : " + resultHashMap);
    }
}
1
Madhav

Oui, vous devez parcourir la table de hachage, sauf si vous implémentez quelque chose qui va dans le sens de ce que suggèrent ces diverses réponses. Plutôt que de manipuler entrySet, j'obtiendrais simplement keySet (), l'itérer sur cet ensemble et je garderais la (première) clé qui vous permet d'obtenir la valeur correspondante. Si vous avez besoin de toutes les clés correspondant à cette valeur, vous devez évidemment tout faire.

Comme Jonas le suggère, c'est peut-être déjà ce que fait la méthode containsValue. Vous pouvez donc ignorer ce test et effectuer l'itération à chaque fois (ou peut-être que le compilateur éliminera déjà la redondance, qui sait).

En outre, par rapport aux autres réponses, si votre carte inversée ressemble à

Map<Value, Set<Key>>

vous pouvez utiliser des mappages non uniques clé-> valeur, si vous avez besoin de cette capacité (en les démêlant de côté). Cela incorporerait très bien l’une des solutions suggérées ici par deux cartes.

1
Carl

Bien que cela ne réponde pas directement à la question, il est lié.

De cette façon, vous n'avez pas besoin de continuer à créer/itérer. Créez simplement une carte inversée une fois et obtenez ce dont vous avez besoin.

/**
 * Both key and value types must define equals() and hashCode() for this to work.
 * This takes into account that all keys are unique but all values may not be.
 *
 * @param map
 * @param <K>
 * @param <V>
 * @return
 */
public static <K, V> Map<V, List<K>> reverseMap(Map<K,V> map) {
    if(map == null) return null;

    Map<V, List<K>> reverseMap = new ArrayMap<>();

    for(Map.Entry<K,V> entry : map.entrySet()) {
        appendValueToMapList(reverseMap, entry.getValue(), entry.getKey());
    }

    return reverseMap;
}


/**
 * Takes into account that the list may already have values.
 * 
 * @param map
 * @param key
 * @param value
 * @param <K>
 * @param <V>
 * @return
 */
public static <K, V> Map<K, List<V>> appendValueToMapList(Map<K, List<V>> map, K key, V value) {
    if(map == null || key == null || value == null) return map;

    List<V> list = map.get(key);

    if(list == null) {
        List<V> newList = new ArrayList<>();
        newList.add(value);
        map.put(key, newList);
    }
    else {
        list.add(value);
    }

    return map;
}
1
Markymark
  1. Si vous voulez obtenir la clé de la valeur, il est préférable d’utiliser bidimap (cartes bidirectionnelles), vous pouvez obtenir la clé de la valeur dans O(1) heure.

    Mais l'inconvénient, c'est que vous ne pouvez utiliser qu'un jeu de clés et de valeurs unique.

  2. Il existe une structure de données appelée Table en Java, qui n'est rien d'autre qu'une carte de cartes comme

    Tableau <A, B, C> == carte <A, carte <B, C>>

    Ici, vous pouvez obtenir map<B,C> en interrogeant T.row(a);, et vous pouvez également obtenir map<A,C> en interrogeant T.column(b);.

Dans votre cas particulier, insérez C comme une constante.

Donc, ça ressemble à <a1, b1, 1> <a2, b2, 1>, ...

Donc, si vous trouvez via T.row (a1) ---> retourne la carte de -> récupère le jeu de clés de cette carte retournée.

Si vous avez besoin de trouver la valeur de la clé, alors T.colonne (b2) -> renvoie la carte de -> obtenir le jeu de clés de la carte renvoyée.

Avantages par rapport au cas précédent: 

  1. Peut utiliser plusieurs valeurs.
  2. Plus efficace lors de l'utilisation de grands ensembles de données.
0
Batman

Soit la valeur maxValue.

Set keySet = map.keySet();

keySet.stream().filter(x->map.get(x)==maxValue).forEach(x-> System.out.println(x));
0
Shivendra Gupta

essaye ça:

static String getKeyFromValue(LinkedHashMap<String, String> map,String value) {
    for (int x=0;x<map.size();x++){
        if( String.valueOf( (new ArrayList<String>(map.values())).get(x) ).equals(value))
            return String.valueOf((new ArrayList<String>(map.keySet())).get(x));
    }
    return null;
}
0
programmer

Je pense que keySet () peut être utile pour trouver les clés mappées sur la valeur et avoir un meilleur style de codage que entrySet ().

Ex:

Supposons que vous ayez un HashMap map, ArrayList res, un valeur que vous voulez trouver toute la clé mappé sur, puis stockez les clés dans le res.

Vous pouvez écrire le code ci-dessous:

    for (int key : map.keySet()) {
        if (map.get(key) == value) {
            res.add(key);
        }
    }

plutôt que d'utiliser entrySet () ci-dessous:

    for (Map.Entry s : map.entrySet()) {
        if ((int)s.getValue() == value) {
            res.add((int)s.getKey());
        }
    }

J'espère que ça aide :)

0
FrancisGeek

Il est important de noter que depuis cette question, Apache Collections prend en charge BidiMaps générique . Ainsi, certaines des réponses les plus votées ne sont plus exactes sur ce point.

Pour un BidiMap sérialisé prenant également en charge les valeurs en double (scénario 1 à plusieurs), prenez également en compte MapDB.org .

0
kervin

Autant que je sache, les clés et les valeurs d'un HashMap ne sont pas mélangées lorsque vous les représentez sous la forme de tableaux:

hashmap.values().toArray()

et

hashmap.keySet().toArray()

Donc, le code suivant (depuis Java 8) devrait fonctionner comme prévu:

public Object getKeyByFirstValue(Object value) {
    int keyNumber =  Arrays.asList(hashmap.values().toArray()).indexOf(value);
    return hashmap.keySet().toArray()[keyNumber];
}

Cependant, ( ATTENTION! ) cela fonctionne 2 à 3 fois plus lentement que l'itération.

0
MilTy