Java Hashmap: cómo obtener la clave del valor?

Si tengo el valor "foo" , y un HashMap ftw para el cual ftw.containsValue("foo") devuelve true , ¿cómo puedo obtener la clave correspondiente? ¿Tengo que recorrer el hashmap? ¿Cuál es la mejor manera de hacer eso?

Si elige usar la biblioteca de Colecciones de Commons en lugar de la API de Colecciones de Java estándar, puede lograrlo con facilidad.

La interfaz de BidiMap en la biblioteca de colecciones es un mapa bidireccional que le permite asignar una clave a un valor (como mapas normales) y también asignar un valor a una clave, lo que le permite realizar búsquedas en ambas direcciones. El método getKey () admite la obtención de una clave para un valor.

Sin embargo, hay una advertencia, los mapas bidi no pueden tener múltiples valores asignados a las claves, y por lo tanto, a menos que su conjunto de datos tenga asignaciones 1: 1 entre claves y valores, no puede usar bidimaps.

Actualizar

Si desea confiar en la API de colecciones de Java, deberá garantizar la relación 1: 1 entre las claves y los valores en el momento de insertar el valor en el mapa. Esto es más fácil dicho que hecho.

Una vez que pueda asegurarse de eso, use el método entrySet () para obtener el conjunto de entradas (mapeos) en el Mapa. Una vez que haya obtenido el conjunto cuyo tipo es Map.Entry , repita las entradas, compare el valor almacenado con el esperado y obtenga la clave correspondiente .

Actualización # 2

El soporte para mapas bidi con generics se puede encontrar en Google Guava y en las bibliotecas de colecciones colectivas refactoradas (este último no es un proyecto Apache). Gracias a Esko por señalar el soporte genérico que falta en las Colecciones de Apache Commons. El uso de colecciones con generics hace que el código sea más fácil de mantener.

Si su estructura de datos tiene un mapeo de varios a uno entre claves y valores, debe repetir las entradas y seleccionar todas las claves adecuadas:

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

En caso de relación uno a uno , puede devolver la primera clave coincidente:

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

En Java 8:

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

Además, para los usuarios de Guava, BiMap puede ser útil. Por ejemplo:

 BiMap tokenToChar = ImmutableBiMap.of(Token.LEFT_BRACKET, '[', Token.LEFT_PARENTHESIS, '('); Token token = tokenToChar.inverse().get('('); Character c = tokenToChar.get(token); 
 public class NewClass1 { public static void main(String[] args) { Map testMap = new HashMap(); testMap.put(10, "a"); testMap.put(20, "b"); testMap.put(30, "c"); testMap.put(40, "d"); for (Entry entry : testMap.entrySet()) { if (entry.getValue().equals("c")) { System.out.println(entry.getKey()); } } } } 

Alguna información adicional … Puede ser útil para usted

El método anterior puede no ser bueno si tu hashmap es realmente grande. Si su hashmap contiene una clave única para el mapeo de valor único, puede mantener un hashmap más que contenga un mapeo de valor a clave.

Es decir, tienes que mantener dos hashmaps

 1. Key to value 2. Value to key 

En ese caso, puede usar el segundo hashmap para obtener la clave.

Puede insertar la clave, el par de valores y su inversa en la estructura de su mapa

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

El uso de map.get (“theValue”) devolverá “theKey”.

Es una manera rápida y sucia que he hecho mapas constantes, que solo funcionarán para unos pocos conjuntos de datos seleccionados:

  • Contiene solo 1 a 1 par
  • El conjunto de valores es disjunto del conjunto de teclas (1-> 2, 2-> 3 lo rompe)

Creo que tus elecciones son

  • Utilice una implementación de mapa creada para esto, como BiMap de las colecciones de google. Tenga en cuenta que las colecciones google BiMap requieren de valores únicos, así como de claves, pero proporcionan un alto rendimiento en ambas direcciones.
  • Mantener manualmente dos mapas: uno para la clave -> valor y otro para el valor -> clave
  • Itera a través de entrySet() y para encontrar las claves que coinciden con el valor. Este es el método más lento, ya que requiere iterar a través de toda la colección, mientras que los otros dos métodos no lo requieren.

Para encontrar todas las claves que se asignan a ese valor, recorra todos los pares en el hashmap, usando map.entrySet() .

No hay una respuesta inequívoca, porque las claves múltiples se pueden asignar al mismo valor. Si está aplicando la exclusividad con su propio código, la mejor solución es crear una clase que use dos Hashmaps para seguir las asignaciones en ambas direcciones.

Decora el mapa con tu propia implementación

 class MyMap extends HashMap{ Map reverseMap = new HashMap(); @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); } } 

Creo que esta es la mejor solución, la dirección original: Java2s

  import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] argv) { Map map = new HashMap(); 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; } } 

Un uso fácil: si pones todos los datos en hasMap y tienes item = “Automóvil”, entonces estás buscando su clave en hashMap. esa es una buena solución.

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

Si construye el mapa en su propio código, intente juntar la clave y el valor en el mapa:

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

Luego, cuando tiene un valor, también tiene la clave.

Me temo que solo tendrás que repetir tu mapa. Lo más corto que pude llegar:

 Iterator> iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); if (entry.getValue().equals(value_you_look_for)) { String key_you_look_for = entry.getKey(); } } 

Parece que la mejor manera es iterar sobre las entradas usando map.entrySet() ya que map.containsValue() probablemente lo haga de todos modos.

 for(int key: hm.keySet()) { if(hm.get(key).equals(value)) { System.out.println(key); } } 

Usando Java 8:

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

Para Android API de segmentación de desarrollo <19, la solución de relación uno a uno de Vitalii Fedorenko no funciona porque Objects.equals no está implementado. Aquí hay una alternativa simple:

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

Puedes usar lo siguiente:

 public class HashmapKeyExist { public static void main(String[] args) { HashMap hmap = new HashMap(); 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); } } } } 

Puede obtener la clave usando valores usando el siguiente código …

 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); 
 public static class SmartHashMap  { public HashMap keyValue; public HashMap valueKey; public SmartHashMap(){ this.keyValue = new HashMap(); this.valueKey = new HashMap(); } 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); } } 
 public static String getKey(Map mapref, String value) { String key = ""; for (Map.Entry map : mapref.entrySet()) { if (map.getValue().toString().equals(value)) { key = map.getKey(); } } return key; } 

Sí, debe recorrer el hashmap, a menos que implemente algo similar a lo que sugieren estas diversas respuestas. En lugar de jugar con el conjunto de entrada, simplemente obtendría el conjunto de claves (), iterar sobre ese conjunto y mantener la (primera) clave que le da el valor correspondiente. Si necesita todas las claves que coinciden con ese valor, obviamente tiene que hacer todo el trabajo.

Como sugiere Jonas, esto podría ser lo que el método containsValue está haciendo, por lo que puede omitir esa prueba en conjunto, y simplemente hacer la iteración todo el tiempo (o tal vez el comstackdor ya eliminará la redundancia, quién sabe).

Además, en relación con las otras respuestas, si su mapa inverso se ve como

 Map> 

puede tratar con asignaciones de valor clave> no únicas, si necesita esa capacidad (desenredándolas). Eso incorporaría bien en cualquiera de las soluciones que las personas sugieren aquí usando dos mapas.

 import java.util.HashMap; import java.util.HashSet; import java.util.Set; public class ValueKeysMap extends HashMap { HashMap> ValueKeysMap = new HashMap>(); @Override public boolean containsValue(Object value) { return ValueKeysMap.containsKey(value); } @Override public V put(K key, V value) { if (containsValue(value)) { Set keys = ValueKeysMap.get(value); keys.add(key); } else { Set keys = new HashSet(); keys.add(key); ValueKeysMap.put(value, keys); } return super.put(key, value); } @Override public V remove(Object key) { V value = super.remove(key); Set keys = ValueKeysMap.get(value); keys.remove(key); if(keys.size() == 0) { ValueKeysMap.remove(value); } return value; } public Set getKeys4ThisValue(V value){ Set keys = ValueKeysMap.get(value); return keys; } public boolean valueContainsThisKey(K key, V value){ if (containsValue(value)) { Set keys = ValueKeysMap.get(value); return keys.contains(key); } return false; } /* * Take care of argument constructor and other api's like putAll */ } 
 /** * This method gets the Key for the given Value * @param paramName * @return */ private String getKeyForValueFromMap(String paramName) { String keyForValue = null; if(paramName!=null)) { Set> entrySet = myMap().entrySet(); if(entrySet!=null && entrySet.size>0) { for(Entry entry : entrySet) { if(entry!=null && paramName.equalsIgnoreCase(entry.getValue())) { keyForValue = entry.getKey(); } } } } return keyForValue; } 
 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> resultHashMap = new HashMap>(); Set newKeyList = resultHashMap.keySet(); for (Iterator iterator = originalHashMap.keySet().iterator(); iterator.hasNext();) { String hashKey = (String) iterator.next(); if (!newKeyList.contains(originalHashMap.get(hashKey))) { List loArrayList = new ArrayList(); loArrayList.add(hashKey); resultHashMap.put(originalHashMap.get(hashKey), loArrayList); } else { List 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); } } 

Use una envoltura delgada: HMap

 import java.util.Collections; import java.util.HashMap; import java.util.Map; public class HMap { private final Map> map; public HMap() { map = new HashMap>(); } public HMap(final int initialCapacity) { map = new HashMap>(initialCapacity); } public boolean containsKey(final Object key) { return map.containsKey(key); } public V get(final Object key) { final Map entry = map.get(key); if (entry != null) return entry.values().iterator().next(); return null; } public K getKey(final Object key) { final Map 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 entry = map .put(key, Collections.singletonMap(key, value)); if (entry != null) return entry.values().iterator().next(); return null; } } 

Mis 2 centavos. Puede obtener las claves en una matriz y luego recorrer la matriz. Esto afectará el rendimiento de este bloque de código si el mapa es bastante grande, donde primero obtendrás las claves en una matriz, lo que podría consumir algo de tiempo y luego estarás bucleando. De lo contrario, para mapas más pequeños, debería estar bien.

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

Si bien esto no responde directamente a la pregunta, está relacionado.

De esta forma no es necesario seguir creando / iterando. Simplemente crea un mapa inverso una vez y obtén lo que necesitas.

 /** * 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  * @param  * @return */ public static  Map> reverseMap(Map map) { if(map == null) return null; Map> reverseMap = new ArrayMap<>(); for(Map.Entry 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  * @param  * @return */ public static  Map> appendValueToMapList(Map> map, K key, V value) { if(map == null || key == null || value == null) return map; List list = map.get(key); if(list == null) { List newList = new ArrayList<>(); newList.add(value); map.put(key, newList); } else { list.add(value); } return map; } 

En java8

 map.entrySet().stream().filter(entry -> entry.getValue().equals(value)) .forEach(entry -> System.out.println(entry.getKey())); 

Es importante tener en cuenta que, desde esta pregunta, Apache Collections admite BidiMaps generics . Por lo tanto, algunas de las principales respuestas votadas ya no son precisas en ese punto.

Para una BidiMap serializada que también admite valores duplicados (escenario de 1 a muchos) también considere MapDB.org .

  1. Si desea obtener la clave del valor, es mejor utilizar bidimap (mapas bidireccionales), puede obtener la clave del valor en O (1) tiempo.

    Pero, el inconveniente de esto es que solo puede usar conjunto de claves único y conjunto de valores.

  2. Hay una estructura de datos llamada Table in java, que no es más que un mapa de mapas como

    Tabla = mapa >

    Aquí puede obtener el map al consultar a T.row(a); , y también puede obtener el map consultando T.column(b);

En su caso especial, inserte C como una constante.

Por lo tanto, es como , …

Entonces, si encuentra a través de T.row (a1) —> devuelve el mapa de -> obtener conjunto de claves, este mapa regresa.

Si necesita encontrar el valor de la clave, T.column (b2) -> devuelve el mapa de -> get keyset del mapa devuelto.

Ventajas sobre el caso anterior:

  1. Puede usar valores múltiples
  2. Más eficiente cuando se usan grandes conjuntos de datos.

Creo que keySet () puede ser útil para encontrar la asignación de claves al valor y tener un mejor estilo de encoding que entrySet () .

Ex:

Supongamos que tiene un mapa HashMap, ArrayList res , un valor en el que desea encontrar todas las asignaciones de teclas, y luego almacena las claves en res .

Puedes escribir el código a continuación:

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

en lugar de usar entrySet () a continuación:

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

Espero eso ayude 🙂