Key existence check in HashMap
是否总是需要在HashMap中检查密钥是否存在?
我有一个HashMap,说1000条目,我正在寻求提高效率。
如果非常频繁地访问HashMap,那么在每次访问时检查密钥是否存在将导致很大的开销。 相反,如果密钥不存在并因此发生异常,我可以捕获异常。 (当我知道这种情况很少发生时)。 这将减少对HashMap的访问一半。
这可能不是一个好的编程习惯,但它会帮助我减少访问次数。 或者我在这里遗漏了什么?
[更新]我在HashMap中没有空值。
-
"因此发生异常" - 有什么例外? 这不是来自java.util.HashMap ...
你有没有存储空值?如果没有,你可以这样做:
1 2 3 4 5 6
| Foo value = map.get(key);
if (value != null) {
...
} else {
// No such key
} |
否则,如果返回null值,则可以检查是否存在:
1 2 3 4 5 6 7 8 9 10 11
| 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
}
} |
-
转到这个问题,解释为什么containsKey在测试密钥时优于get。
-
@Samuel:仅当null为可能值时。如果你在地图中肯定没有空值,只需get即可,并且当你需要这个值时也避免做两次查找。
-
虽然这可能更清楚,但您也可以为第二部分编写if(value!=null || map.containsKey(key))。至少如果你想以同样的方式做同样的事情 - 没有重复的代码。它会因短路而起作用。
通过检查密钥是否存在,您将无法获得任何收益。这是HashMap的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @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源代码。
资源:
-
HashMap源代码 s>糟糕的一个
-
HashMap源代码很好
-
显示这些方法的一个具体实现有什么意义?
-
为了解释这种情况,在大多数情况下,检查密钥存在与获取值大致相同。因此,在获取值之前,不会优化任何内容来检查实际存在的密钥。我知道这是一种概括,但它有助于理解。
-
一个很好的链接是grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/(OpenJDK非常强烈地源自Sun代码),似乎我错了。我正在将Java5的版本与Java6进行比较;他们在这方面的工作方式不同(但两者都是正确的,你发布的片段也是如此)。
-
@Donal Fellows,谢谢,我用它更新了我的帖子。
-
正如在接受的答案中指出的那样,这个问题是完全错误的。当然,您通过检查比较值的关键存在来获得某些东西 - 您可以将不存在的键与存在的键区分开,但将其映射为null作为值。
更好的方法是使用HashMap的containsKey方法。明天有人会向地图添加null。您应该区分密钥存在和密钥具有空值。
-
是啊。或者继承HashMap以防止共存null。
-
1+用于原始类型,因为使用此答案不需要使用不必要的值
-
编写.containsKey()比检查null更流畅。在大多数情况下,我们应该更加关注易于阅读,这可以节省开发人员的时间,而不是一些小的优化。至少在必要之前不进行优化。
你是说你有像这样的代码
if(map.containsKey(key)) doSomethingWith(map.get(key))
到处都是 ?然后你应该检查map.get(key)是否返回null,就是这样。
顺便说一下,HashMap不会为丢失的密钥抛出异常,而是返回null。需要containsKey的唯一情况是,当您存储空值时,要区分空值和缺失值,但这通常被认为是不好的做法。
只需使用containsKey()即可。它速度快,保持代码清洁和可读性。 HashMap s的重点是密钥查找速度很快,只需确保hashCode()和equals()正确实现。
1
| if(map.get(key) != null || (map.get(key) == null && map.containsKey(key))) |
您还可以在HashMap类中使用computeIfAbsent()方法。
在以下示例中,Map存储应用于密钥的事务(整数)列表(银行帐户的名称)。要将100和200的2个事务添加到checking_account,您可以编写:
1 2 3 4
| HashMap <String, ArrayList <Integer >> map = new HashMap <>();
map. computeIfAbsent("checking_account", key -> new ArrayList <>())
. add(100)
. add(200); |
这样您就不必检查密钥checking_account是否存在。
-
如果它不存在,将由lambda表达式创建并返回一个。
-
如果存在,则键的值将由computeIfAbsent()返回。
真的很优雅! ??
Jon Skeet的答案很好地解决了这两种情况(使用null值而不是null值映射)。
关于数字条目和效率问题,我想补充一些东西。
I have a HashMap with say a 1.000 entries and I am looking at improving
the efficiency. If the HashMap is being accessed very frequently, then
checking for the key existence at every access will lead to a large
overhead.
包含1.000个条目的地图不是一张巨大的地图。
以及具有5.000或10.000条目的地图。
Map旨在通过这样的尺寸进行快速检索。
现在,它假设地图键的hashCode()提供了良好的分布。
如果您可以使用Integer作为密钥类型,请执行此操作。
它的hashCode()方法非常有效,因为唯一的int值不可能发生冲突:
1 2 3 4 5 6 7 8 9 10 11 12
| 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中经常使用的,你可能会有一些碰撞,但在Map中有一千到几千个对象,你应该只有很少的,因为String.hashCode()方法提供了良好的分布。
如果您使用自定义类型,请正确覆盖hashCode()和equals(),并确保hashCode()提供公平分配。
您可以参考Java Effective的第9项引用它。
这是一篇详细说明方式的帖子。
如果是关键类,请确保实现了hashCode()和equals()方法。
基本上,对HashMap的访问应该是O(1),但是使用错误的hashCode方法实现它变为O(n),因为具有相同散列键的值将存储为链接列表。
我通常使用这个成语
1 2 3 4 5
| Object value = map. get(key );
if (value == null) {
value = createValue (key );
map. put(key, value );
} |
这意味着如果缺少密钥,则只会按两次地图