Проверка наличия ключа в 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, который подробно описывает путь.