Map.containsKey: примеры (JAVA)
Map.containsKey(Object key): booleanОбщее описание Map.containsKey
Метод Map.containsKey(Object key) проверяет, содержит ли отображение (Map) ключ, равный заданному. Возвращается булево значение: true если имеется соответствие ключа, false в противном случае. Метод объявлен в интерфейсе java.util.Map и реализуется всеми стандартными реализациями коллекций.
Параметр:
- key - объект ключа для проверки. Может быть
nullдля тех реализаций, которые поддерживают null-ключи (например, HashMap, LinkedHashMap). Некоторые реализации (ConcurrentHashMap, TreeMap при естественном порядке) не допускают null и при передаче null выбрасывают исключение.
Возвращаемое значение:
- boolean -
trueесли в Map присутствует запись с ключом, который сравнивается с переданным с помощью методаequals(и для хеш-основных реализаций также используетсяhashCode); в противном случаеfalse.
Поведение и сложность:
- Для HashMap и аналогичных реализаций среднее время проверки - O(1), в худшем случае - O(n).
- Для TreeMap время - O(log n) за счёт поиска по дереву.
- Для ConcurrentMap реализация обеспечивает конкурентную безопасность, но некоторые операции могут иметь ограничения на null-ключи.
Особенности сравнения ключей:
- Для большинства реализаций используется метод
equalsдля сравнения ключей. В IdentityHashMap сравнение выполняется по идентичности (==), а не поequals. - Неправильные реализации методов
equalsиhashCodeу объектов-ключей приводят к неверным результатам при поиске ключа.
Примеры использования containsKey
Примеры с выводом кода и результата.
1) Базовый пример с HashMap:
import java.util.*;
Map map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
System.out.println(map.containsKey("a"));
System.out.println(map.containsKey("c"));
true false
2) Проверка null-ключа (HashMap допускает):
Map map = new HashMap<>();
map.put(null, 5);
System.out.println(map.containsKey(null));
true
3) TreeMap и null-ключ вызывает исключение (при естественном порядке):
Map map = new TreeMap<>();
map.put("x", 10);
System.out.println(map.containsKey(null));
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.TreeMap.getEntry(TreeMap.java:...)
...
4) Отличие от get при значении null:
Map map = new HashMap<>();
map.put("k", null);
System.out.println(map.get("k") == null);
System.out.println(map.containsKey("k"));
System.out.println(map.get("missing") == null);
System.out.println(map.containsKey("missing"));
true true true false
5) Пользовательский ключ с переопределёнными equals и hashCode:
class Key { String id; Key(String id){this.id=id;}
public boolean equals(Object o){return o instanceof Key && ((Key)o).id.equals(id);}
public int hashCode(){return id.hashCode();} }
Map map = new HashMap<>();
map.put(new Key("1"), "one");
System.out.println(map.containsKey(new Key("1")));
true
Похожие методы в Java и их особенности
- Map.get(Object key) - возвращает значение по ключу. При отсутствии возвращается
null, что даёт неоднозначность, если значения могут быть null. - Map.containsValue(Object value) - проверяет наличие значения. Обычно дороже по времени (O(n)).
- Map.keySet().contains(Object key) - эквивалент
containsKeyдля стандартных реализаций, может быть удобен при работе с представлением ключей. - ConcurrentMap.putIfAbsent / computeIfAbsent - предпочтительнее в многопоточных сценариях вместо последовательной проверки
containsKeyи вставки, так как обеспечивают атомарность.
Когда что предпочтительнее:
- Если требуется только проверка наличия ключа -
containsKey. - Если нужно значение без неоднозначности null - использовать
containsKeyсовместно сget, либо хранить Optional или использоватьgetOrDefault. - В конкурентных сценариях - методам атомических операций (putIfAbsent, computeIfAbsent) отдать предпочтение перед containsKey+put.
Аналоги в других языках и их отличия
Ниже короткие примеры и комментарии для разных языков.
- PHP - array_key_exists и isset. array_key_exists проверяет существование ключа даже если значение равно null, isset возвращает false для null-значений.
$arr = ["a" => null]; var_dump(array_key_exists("a", $arr)); var_dump(isset($arr["a"]));bool(true) bool(false)
- JavaScript - Map.prototype.has и оператор in для объектов. Map.has точен для Map; оператор in проверяет свойства объекта и унаследованные свойства.
const m = new Map([["a",1]]); console.log(m.has("a")); console.log("a" in {a:1});true true
- Python - оператор in или dict.__contains__. Возвращает True если ключ присутствует; dict.get возвращает None для отсутствия или для значения None.
d = {"a": None} print("a" in d) print(d.get("a") is None) print("b" in d)True True False
- SQL - присутствие ключа обычно проверяется через EXISTS или WHERE ... IN.
-- пример SELECT EXISTS(SELECT 1 FROM table WHERE id = 5);-- возвращает true/false в зависимости от СУБД
- C# - Dictionary<K,V>.ContainsKey и TryGetValue. TryGetValue безопаснее при возможных null-значениях и в многопоточных сценариях.
var dict = new DictionaryTrue True
- Lua - таблицы: наличие ключа проверяется через сравнение с nil. Если значение может быть nil, применяется метаполя или отдельная структура.
t = {a = nil} print(t["a"] ~= nil) print(rawget(t, "a") == nil)false true
- Go - idiomatic: value, ok := m[key]; ok показывает наличие ключа даже если значение zero-value.
m := map[string]*int{"a": nil} _, ok := m["a"] fmt.Println(ok) _, ok2 := m["b"] fmt.Println(ok2)true false
- Kotlin - Map.containsKey, работает аналогично Java. Kotlin предоставляет удобные расширения, например getValue или getOrElse.
Отличия от Java: в некоторых языках (PHP isset, Lua) проверка ложная при значении null; в Go проверка возвращает булев флаг отдельно; в JavaScript объекты и Map различаются по поведению при проверке ключей.
Типичные ошибки при использовании containsKey
- Ошибочное использование get для проверки присутствия: проверка
map.get(key) == nullне отличает отсутствие ключа от значенияnull. Пример выше показывает отличия. - Использование
nullв реализациях, которые не допускают null-ключи: например, ConcurrentHashMap и TreeMap при естественном порядке выбросят NullPointerException.Mapmap = new java.util.concurrent.ConcurrentHashMap<>(); map.containsKey(null); Exception in thread "main" java.lang.NullPointerException at java.base/java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:...) ... - Неправильная реализация equals/hashCode у ключей: если hashCode или equals меняются после вставки в HashMap, containsKey может вернуть false.
class Key { String id; Key(String id){this.id=id;} public int hashCode(){return id.length();} public boolean equals(Object o){return o instanceof Key && ((Key)o).id.equals(id);} } Mapm = new HashMap<>(); Key k = new Key("ab"); m.put(k, "v"); // изменена внутренне id (симулируется)/или состояние, влияющее на hashCode k.id = "abcd"; System.out.println(m.containsKey(k)); false
- ClassCastException при использовании TreeMap с несравнимыми ключами.
Map map = new TreeMap(); map.put("a",1); map.containsKey(10);Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at java.base/java.util.TreeMap.compare(TreeMap.java:...) ...
Изменения в API и заметные отличия в новых версиях
Метод containsKey присутствует в интерфейсе java.util.Map с ранних версий платформы (Collections framework). За последние версии Java сам метод не претерпел изменений в сигнатуре и семантике.
Косвенные изменения затрагивают сопутствующие возможности API: с появлением Java 8 и выше добавлены методы getOrDefault, computeIfAbsent, forEach и другие функциональные методы, которые в ряде случаев заменяют комбинацию containsKey и последующих действий. В многопоточном коде предпочтение отдаётся атомарным операциям ConcurrentMap.
Расширенные и редкие сценарии применения
Несколько углублённых примеров с объяснениями.
1) Использование IdentityHashMap - сравнение ключей по ссылке:
import java.util.*;
Map m = new IdentityHashMap<>();
String a1 = new String("x");
String a2 = new String("x");
m.put(a1, "v");
System.out.println(m.containsKey(a1));
System.out.println(m.containsKey(a2));
true false
Объяснение: IdentityHashMap использует сравнение по ==, поэтому равные по содержимому строки, но разные объекты считаются разными ключами.
2) WeakHashMap и влияние сборки мусора на наличие ключа:
import java.util.*;
Map
true true // возможно true, если ключ был очищен сборщиком мусора
Объяснение: WeakHashMap хранит ключи с слабой ссылкой, поэтому после удаления внешней ссылки и выполнения GC запись может исчезнуть и containsKey вернёт false.
3) Атомарность в многопоточной вставке - гонка при containsKey + put:
Map map = new java.util.concurrent.ConcurrentHashMap<>();
// поток A
if (!map.containsKey("k")) {
// до put другой поток может вставить ту же запись
map.put("k","v1");
}
// безопаснее:
map.putIfAbsent("k","v2");
// при использовании containsKey возможны дубликаты вставок от разных потоков // putIfAbsent гарантирует, что первый вставивший останется
Объяснение: containsKey не обеспечивает атомарности с последующими операциями. В многопоточных сценариях лучше использовать putIfAbsent или computeIfAbsent.
4) Поиск с пользовательским сравнением через ключ-обёртку:
// хранение по naturalHash, но поиск по другому критерию
class Wrapper { String id; Wrapper(String id){this.id=id;}
public boolean equals(Object o){return o instanceof Wrapper && ((Wrapper)o).id.equalsIgnoreCase(id);}
public int hashCode(){return id.toLowerCase().hashCode();} }
Map map = new HashMap<>();
map.put(new Wrapper("Ab"), "v");
System.out.println(map.containsKey(new Wrapper("ab")));
true
Объяснение: если требуется нечувствительный к регистру поиск, реализация equals/hashCode может это учесть.
5) Использование containsKey в stream-выражениях:
Map map = Map.of("a",1,"b",2);
List keysToCheck = List.of("a","c");
List present = keysToCheck.stream()
.filter(map::containsKey)
.toList();
System.out.println(present);
[a]
Объяснение: метод можно передавать как ссылку на метод для компактной фильтрации коллекций ключей.
6) Нестандартный случай: Map с пользовательской стратегией равенства - пример для ConcurrentHashMap не возможен, но для других реализаций есть адаптеры, поэтому при выборе реализации важно учитывать требования к сравнению ключей.