Java Hashmap: как получить ключ от value?

если у меня есть значение "foo" и HashMap<String> ftw для чего ftw.containsValue("foo") возвращает true, как я могу получить соответствующий ключ? Должен ли я проходить через HashMap? Как лучше всего это сделать?

30 ответов


Если вы решите использовать библиотека общих собраний вместо стандартного API Java Collections, вы можете достичь этого с легкостью.

на BidiMap интерфейс в библиотеке коллекций представляет собой двунаправленную карту, позволяющую сопоставить ключ со значением (например, обычные карты), а также сопоставить значение с ключом, что позволяет выполнять поиск в обоих направлениях. Получение ключа для значения поддерживается параметром getKey() метод.

есть предостережение, хотя карты bidi не могут иметь несколько значений, сопоставленных ключам, и, следовательно, если ваш набор данных не имеет 1:1 сопоставления между ключами и значениями, вы не можете использовать bidimaps.

обновление

Если вы хотите полагаться на API Java Collections, вам нужно будет обеспечить соотношение 1:1 между ключами и значениями во время вставки значения в карту. Это легче сказать, чем сделать.

Как только вы сможете убедитесь, что используйте entrySet() метод для получения набора записей (отображений) на карте. После того как вы получили набор типа карта.Запись, повторите записи, сравнивая сохраненное значение против ожидаемого, и получить соответствующую клавишу.

обновление #2

Поддержка карт bidi с дженериками можно найти в Google Guava и рефакторингу Коммонс-Коллекции библиотеки (последний не является проектом Apache). Спасибо Esko за указание отсутствующей универсальной поддержки в коллекциях Apache Commons. Использование коллекций с дженериками делает код более доступным для обслуживания.


если ваша структура данных многие-к-одному сопоставление между ключами и значениями вы должны перебирать записи и выбирать все подходящие ключи:

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

в случае один на один отношения, вы можете вернуть первый согласованный ключ:

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

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

кроме того, для пользователей Guava,BiMap могут быть полезны. Например:

BiMap<Token, Character> 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<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());
            }
        }
    }
}

дополнительная информация... Может быть вам полезно

выше метод не может быть хорошим, если ваш hashmap действительно большой. Если хэш-карта содержит уникальный ключ для сопоставления уникальных значений, можно сохранить еще одну хэш-карту, содержащую сопоставление от значения к ключу.

то есть вы должны поддерживать два hashmaps

1. Key to value

2. Value to key 

в этом случае вы можете использовать второй hashmap для получения ключа.


вы можете вставить как ключ, пару значений, так и ее обратную в структуру карты

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

используя карту.get ("theValue") затем вернет"ключ".

это быстрый и грязный способ, которым я сделал постоянные карты, которые будут работать только для нескольких наборов данных:

  • содержит только от 1 до 1 Пар
  • набор значений не связан с набором ключей (1->2, 2->3 ломает его)

Я думаю, что ваш выбор

  • используйте реализацию карты, построенную для этого, например BiMap из коллекций google. Обратите внимание, что Google collections bimap требует uniqueless значений, а также ключей, но обеспечивает высокую производительность в обоих направлениях производительности
  • вручную поддерживать две карты-одна для ключа - > значение, а другая карта для значения - > ключ
  • перебора entrySet() и найти ключи, которые соответствуют значению. Это самый медленный метод, так как он требует итерации по всей коллекции, в то время как другие два метода этого не требуют.

чтобы найти все ключи, которые сопоставляются с этим значением, повторите все пары в hashmap, используя map.entrySet().


однозначного ответа нет, потому что несколько ключей могут сопоставляться с одним и тем же значением. Если вы применяете unique-ness с вашим собственным кодом, лучшим решением является создание класса, который использует два Hashmaps для отслеживания сопоставлений в обоих направлениях.


украсьте карту своей собственной реализацией

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

Я думаю, что это лучшее решение, оригинальный адрес: 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;
          }
        }

простое использование: если вы поместите все данные в hasMap, и у вас есть item = "Automobile", поэтому вы ищете его ключ в hashMap. это хорошее решение.

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

Если вы создаете карту в своем собственном коде, попробуйте объединить ключ и значение на карте:

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

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

тогда, когда у вас есть значение, у вас также есть ключ.


боюсь, вам просто придется повторить вашу карту. Самое короткое, что я смог придумать:

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

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

похоже, что лучший способ-это перебирать записи с помощью map.entrySet() с map.containsValue() вероятно, делает это в любом случае.


Использование Java 8:

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

для разработки Android API таргетинга Objects.equals не выполнен. Вот простая альтернатива:

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

вы можете использовать следующие:

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

вы можете получить ключ, используя значения, используя следующий код..

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

}

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

да, вам нужно пройти через hashmap, если вы не реализуете что-то в соответствии с тем, что предлагают эти различные ответы. Вместо того, чтобы возиться с entrySet, я бы просто получил keySet (), перебрал этот набор и сохранил (первый) ключ, который дает вам соответствующее значение. Если вам нужны все ключи, которые соответствуют этому значению, очевидно, вы должны сделать все это.

Как предполагает Джонас, это может быть то, что делает метод containsValue, поэтому вы можете просто пропустите этот тест все вместе и просто делайте итерацию каждый раз (или, возможно, компилятор уже устранит избыточность, кто знает).

кроме того, относительно других ответов, если ваша обратная карта выглядит как

Map<Value, Set<Key>>

вы можете иметь дело с не уникальным ключом->сопоставлениями значений, если вам нужна эта возможность (распутывая их в сторону). Это включило бы штраф в любое из решений, которые люди предлагают здесь, используя две карты.


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
     */
}

/**
 * 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;
}

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

используйте тонкую обертку: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;
   }
}

мои 2 цента. Вы можете получить ключи в массив, а затем перебрать массив. Это повлияет на производительность этого блока кода, если карта довольно большая, где вы сначала получаете ключи в массиве, который может занять некоторое время, а затем вы зацикливаетесь. В противном случае для небольших карт это должно быть нормально.

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

хотя это напрямую не отвечает на вопрос, это связано.

таким образом, вам не нужно продолжать создавать / повторять. Просто создайте обратную карту один раз и получите то, что вам нужно.

/**
 * 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;
}

в java8

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

важно отметить, что, поскольку этот вопрос, коллекции Apache поддерживают Общие BidiMaps. Таким образом, некоторые из лучших проголосовавших ответов больше не точны в этом вопросе.

для сериализации BidiMap, что также поддерживает повторяющиеся значения ( 1-ко-многим сценарий ) тоже считаю MapDB.org.


  1. если вы хотите получить ключ от значения, лучше всего использовать bidimap(двунаправленные карты) , вы можете получить ключ от значения в O (1) раз.

    но недостатком это можно использовать только уникальных ключей и valueset с.

  2. существует структура данных под названием стол в java, который не что иное, как карта карт, таких как

    таблица == карта >

    здесь вы можете получить map<B,C> запрос T.row(a);, и вы также можете получить map<A,C> запрос T.column(b);

в вашем частном случае вставьте C как некоторую константу.

так как , ...

Итак, если вы найдете через T. row(a1) ---> возвращает карту --> get keyset эту возвращенную карту.

Если вам нужно найти значение ключа, то T. столбец (b2) --> возвращает карту --> получить набор ключей возвращенной карты.

преимущества перед предыдущий случай:

  1. можно использовать несколько значений.
  2. более эффективно при использовании больших наборов данных.

Я думаю keySet () может быть хорошо найти сопоставление ключей со значением и иметь лучший стиль кодирования, чем entrySet().

Ex:

Предположим, у вас есть HashMap карта, ArrayList res, a стоимостью вы хотите найти все ключ отображение, а затем хранить ключи к res.

вы можете написать код ниже:

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

вместо использования entrySet() ниже:

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

надеюсь, что это помогает :)