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

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

Ответ 1

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

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

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

Обновление

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

То есть вам нужно сохранить два hashmaps

1. Key to value

2. Value to key 

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

Ответ 4

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

  • Используйте для этого реализацию карты, например BiMap из коллекций google. Обратите внимание, что в коллекциях google BiMap требуются уникальные значения, а также ключи, но он обеспечивает высокую производительность в обоих направлениях производительности.
  • Вручную сохранить две карты - по одному для ключа → значение и другую карту для значения → ключ
  • Перейдите через entrySet() и найдите ключи, соответствующие этому значению. Это самый медленный метод, поскольку он требует итерации по всей коллекции, в то время как другие два метода не требуют этого.

Ответ 5

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

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

Использование map.get( "theValue" ) вернет "theKey".

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

  • Содержит только от 1 до 1 пары.
  • Набор значений не пересекается с набором клавиш (1- > 2, 2- > 3 разрывает его)

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

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

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

Ответ 11

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

Ответ 12

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

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

Ответ 13

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

Ответ 14

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

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

Ответ 15

Для Android-ориентированного таргетинга API < 19, решение отношения "один к одному" Виталия Федоренко не работает, потому что 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;
}

Ответ 16

Вы можете использовать ниже:

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

Ответ 17

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

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

Ответ 18

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

}

Ответ 19

В java8

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

Ответ 20

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

Ответ 21

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

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

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

Map<Value, Set<Key>>

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

Ответ 22

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

Ответ 23

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

Ответ 24

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

Ответ 25

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

Ответ 26

Мои 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)            
}

Ответ 27

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

Пример:

Предположим, что у вас есть карта HashMap, ArrayList res, значение, которое вы хотите найти для всех ключевых, а затем сохраните ключи для 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());
        }
    }

Надеюсь, это поможет:)

Ответ 28

Хотя это не дает прямого ответа на вопрос, оно связано.

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

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

Ответ 29

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

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

Ответ 30

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

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

  • В java есть структура данных, называемая Таблица, которая представляет собой не что иное, как карту карт типа

    Таблица < A, B, C > == map < A, map < B, C →

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

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

Итак, это как < a1, b1, 1 > < a2, b2, 1 > ,...

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

Если вам нужно найти значение ключа, то T.column(b2) → возвращает карту → get keyset возвращенной карты.

Преимущества по сравнению с предыдущим случаем:

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