Проверка существования ключа в HashMap - PullRequest
286 голосов
/ 02 сентября 2010

Всегда ли необходима проверка на наличие ключей в HashMap?

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

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

[ Обновление ] У меня нет нулевых значений в HashMap.

Ответы [ 10 ]

488 голосов
/ 02 сентября 2010

Вы когда-нибудь хранили нулевое значение? Если нет, вы можете просто сделать:

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // No such key
}

В противном случае вы могли бы просто проверить существование, если получили возвращенное нулевое значение:

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // Key might be present...
    if (map.containsKey(key)) {
       // Okay, there's a key but the value is null
    } else {
       // Definitely no such key
    }
}
63 голосов
/ 02 сентября 2010

Вы ничего не получите, проверив, что ключ существует.Это код HashMap:

@Override
public boolean containsKey(Object key) {
    Entry<K, V> m = getEntry(key);
    return m != null;
}

@Override
public V get(Object key) {
    Entry<K, V> m = getEntry(key);
    if (m != null) {
        return m.value;
    }
    return null;
}

Просто проверьте, отличается ли возвращаемое значение для get() от null.

Это исходный код HashMap.


Ресурсы:

39 голосов
/ 02 сентября 2010

Лучше всего использовать метод containsKey HashMap.Завтра soembody добавит на карту ноль, вы должны различать ключ там и ключ имеет нулевое значение.

22 голосов
/ 02 сентября 2010

Вы имеете в виду, что у вас есть код типа

if(map.containsKey(key)) doSomethingWith(map.get(key))

повсюду?Затем вы должны просто проверить, вернул ли map.get(key) ноль, и все.Кстати, HashMap не генерирует исключения для отсутствующих ключей, вместо этого он возвращает null.Единственный случай, когда требуется containsKey, это когда вы храните нулевые значения, чтобы различать нулевое значение и пропущенное значение, но обычно это считается плохой практикой.

7 голосов
/ 02 сентября 2010

Просто используйте containsKey() для ясности.Это быстро и сохраняет код чистым и читабельным.Весь смысл HashMap s в том, что поиск клавиш выполняется быстро, просто убедитесь, что hashCode() и equals() правильно реализованы.

4 голосов
/ 10 ноября 2015
if(map.get(key) != null || (map.get(key) == null && map.containsKey(key)))
0 голосов
/ 26 ноября 2018

Вы также можете использовать метод computeIfAbsent() в классе HashMap.

В следующем примере map хранит список транзакций (целых чисел), которые применяются к ключу (имя банковского счета). Чтобы добавить 2 транзакции 100 и 200 к checking_account, вы можете написать:

HashMap<String, ArrayList<Integer>> map = new HashMap<>();
map.computeIfAbsent("checking_account", key -> new ArrayList<>())
   .add(100)
   .add(200);

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

  • Если он не существует, он будет создан и возвращен лямбда-выражением.
  • Если он существует, то значение ключа будет возвращено computeIfAbsent().

Действительно элегантно! ?

0 голосов
/ 20 октября 2017

Ответ Джона Скита эффективно обращается к двум сценариям (отображение со значением null, а не null значение) эффективным способом.

О числовых записяхи что касается эффективности, я хотел бы добавить кое-что.

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

Карта с 1000 записей не является большой картой.
Такжекак карта с 5.000 или 10.000 записей.
Map предназначены для быстрого поиска с такими размерами.

Теперь предполагается, что hashCode() ключей карты обеспечиваетхорошее распределение.

Если вы можете использовать Integer в качестве типа ключа, сделайте это.
Его hashCode() метод очень эффективен, так как коллизии невозможны для уникального intзначения:

public final class Integer extends Number implements Comparable<Integer> {
    ...
    @Override
    public int hashCode() {
        return Integer.hashCode(value);
    }

    public static int hashCode(int value) {
        return value;
    }
    ...
}

Если для ключа вам нужно использовать другой встроенный тип как String, например, который часто используется в Map, у вас могут быть некоторые коллизии, но от 1 тысячи донесколько тысяч объектов в Map, их должно быть очень мало, так как метод String.hashCode() обеспечивает хорошее распределение.

Если вы используете пользовательский тип, переопределите hashCode() и equals() правильно и в целом убедитесь, что hashCode()обеспечивает справедливое распределение.
Вы можете сослаться на пункт 9 из Java Effective ссылается на него.
Вот сообщение , в котором подробно описан путь.

0 голосов
/ 02 сентября 2010
  1. Если вам нужен ключевой класс, убедитесь, что реализованы методы hashCode () и equals ().
  2. По сути, доступ к HashMap должен быть O (1), но при неправильной реализации метода hashCode он становится O (n), потому что значение с тем же ключом хеша будет сохранено как связанный список.
0 голосов
/ 02 сентября 2010

Я обычно использую идиому

Object value = map.get(key);
if (value == null) {
    value = createValue(key);
    map.put(key, value);
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...