В течение некоторого времени я задавался вопросом, допустимо ли в рамках передовой практики воздерживаться от использования containsKey()
метода java.util.Map
и вместо этого выполнять нулевую проверку результата из get()
.
Мое объяснение состоит в том, что кажется излишним выполнять поиск значения дважды - сначала для, containsKey()
а затем еще раз для get()
.
С другой стороны, может случиться так, что большинство стандартных реализаций Map
кэшируют последний поиск или что компилятор может иным образом избавиться от избыточности, и что для читабельности кода предпочтительнее сохранить containsKey()
часть.
Буду очень признателен за ваши комментарии.
источник
null
, хотите ли вы относиться к нему иначе, чем к неустановленному ключу / значению? Если вам специально не нужно относиться к нему по-другому, вы можете просто использоватьget()
Map
это такprivate
, ваш класс может гарантировать, что anull
никогда не будет вставлен на карту. В этом случае вы можете использовать сget()
последующей проверкой на нуль вместоcontainsKey()
. В некоторых случаях это может быть более понятным и, возможно, немного более эффективным.Я думаю, что довольно стандартно написать:
Object value = map.get(key); if (value != null) { //do something with value }
вместо того
if (map.containsKey(key)) { Object value = map.get(key); //do something with value }
Он не менее читабелен и немного более эффективен, поэтому я не вижу причин не делать этого. Очевидно, что если ваша карта может содержать значение null, эти два параметра не имеют одинаковой семантики .
источник
Как указал ассилий, это семантический вопрос. Как правило, Map.get (x) == null - это то, что вам нужно, но есть случаи, когда важно использовать containsKey.
Один из таких случаев - тайник. Однажды я работал над проблемой производительности в веб-приложении, которое часто запрашивало свою базу данных в поисках несуществующих сущностей. Когда я изучил код кеширования для этого компонента, я понял, что он запрашивает базу данных, если cache.get (key) == null. Если база данных вернула значение null (объект не найден), мы бы кэшировали этот ключ -> сопоставление null.
Переход на containsKey решил проблему, потому что сопоставление с нулевым значением действительно что-то значило. Отображение ключа на null имеет другое семантическое значение, чем несуществующий ключ.
источник
containsKey
с последующим aget
является избыточным, только если мы знаем априори, что нулевые значения никогда не будут разрешены. Если нулевые значения недействительны, вызовcontainsKey
имеет нетривиальное снижение производительности и просто накладные расходы, как показано в тесте ниже.Optional
Идиомы Java 8 -Optional.ofNullable(map.get(key)).ifPresent
илиOptional.ofNullable(map.get(key)).ifPresent
- несут нетривиальные накладные расходы по сравнению с обычными нулевыми проверками.A
HashMap
используетO(1)
постоянный поиск в таблице, тогда как aTreeMap
используетO(log(n))
поиск.containsKey
Следуетget
идиомы гораздо медленнее при вызове наTreeMap
.Контрольные точки
См. Https://github.com/vkarun/enum-reverse-lookup-table-jmh
// t1 static Type lookupTreeMapNotContainsKeyThrowGet(int t) { if (!lookupT.containsKey(t)) throw new IllegalStateException("Unknown Multihash type: " + t); return lookupT.get(t); } // t2 static Type lookupTreeMapGetThrowIfNull(int t) { Type type = lookupT.get(t); if (type == null) throw new IllegalStateException("Unknown Multihash type: " + t); return type; } // t3 static Type lookupTreeMapGetOptionalOrElseThrow(int t) { return Optional.ofNullable(lookupT.get(t)).orElseThrow(() -> new IllegalStateException("Unknown Multihash type: " + t)); } // h1 static Type lookupHashMapNotContainsKeyThrowGet(int t) { if (!lookupH.containsKey(t)) throw new IllegalStateException("Unknown Multihash type: " + t); return lookupH.get(t); } // h2 static Type lookupHashMapGetThrowIfNull(int t) { Type type = lookupH.get(t); if (type == null) throw new IllegalStateException("Unknown Multihash type: " + t); return type; } // h3 static Type lookupHashMapGetOptionalOrElseThrow(int t) { return Optional.ofNullable(lookupH.get(t)).orElseThrow(() -> new IllegalStateException("Unknown Multihash type: " + t)); }
Ссылка на источник TreeMap
https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/TreeMap.java
Ссылка на исходный код HashMap
https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/HashMap.java
источник
Мы можем сделать ответ @assylias более читабельным с помощью Java8 Optional,
Optional.ofNullable(map.get(key)).ifPresent(value -> { //do something with value };)
источник
В Java, если вы проверите реализацию
public boolean containsKey(Object key) { return getNode(hash(key), key) != null; } public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
оба используют getNode для получения соответствия, где и выполняется основная работа.
избыточность является контекстной, например, если у вас есть словарь, хранящийся в хеш-карте. Когда вы хотите узнать значение слова
делаю ...
if(dictionary.containsKey(word)) { return dictionary.get(word); }
избыточно.
но если вы хотите проверить, действительно ли слово, на основе словаря. делаю ...
return dictionary.get(word) != null;
над...
return dictionary.containsKey(word);
избыточно.
Если вы проверяете реализацию HashSet , которая использует HashMap внутри, используйте метод containsKey в методе contains.
public boolean contains(Object o) { return map.containsKey(o); }
источник