Проверка наличия ключа в HashMap

Всегда ли проверяется наличие ключа в HashMap?

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

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

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

Ответ 1

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

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

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

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // Key might be present...
    if (map.containsKey(key)) {
       // Okay, there a key but the value is null
    } else {
       // Definitely no such key
    }
}

Ответ 2

Вы ничего не получите, проверив, что ключ существует. Это код 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.


Ресурсы:

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

if(map.get(key) != null || (map.get(key) == null && map.containsKey(key)))

Ответ 7

Вы также можете использовать метод 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().

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

Ответ 8

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

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

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

Ответ 9

  • Если класс ключа - это ваш, убедитесь, что реализованы методы hashCode() и equals().
  • В принципе, доступ к HashMap должен быть O (1), но с неправильной реализацией метода hashCode он становится O (n), потому что значение с тем же хэш-ключом будет сохранено как Связанный список.

Ответ 10

Ответ на Jon Skeet эффективно описывает два сценария (карта с null значением, а не null).

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

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

Карта с 1.000 записями не является огромной картой. А также карта с 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. Здесь post, который подробно описывает путь.