检查 HashMap 中的键是否存在总是必要的吗?
我有一个包含 1000 个条目的 HashMap,我正在考虑提高效率。 如果HashMap的访问非常频繁,那么每次访问时都检查键是否存在将导致很大的开销。相反,如果密钥不存在并因此发生异常,我可以捕获异常。 (当我知道这种情况很少发生时)。这将使对 HashMap 的访问减少一半。
这可能不是一个好的编程实践,但它将帮助我减少访问次数。或者我在这里遗漏了什么?
[更新] 我的 HashMap 中没有空值。
你曾经存储过空值吗?如果没有,你可以这样做:
Foo value = map.get(key);
if (value != null) {
...
} else {
// No such key
}
否则,如果返回空值,您可以只检查是否存在:
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
}
}
通过检查密钥是否存在你不会获得任何东西。这是
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源代码。
资源:
更好的方法是使用
containsKey
的HashMap
方法。明天有人会将 null 添加到 Map 中。您应该区分密钥存在和密钥具有空值。
你的意思是你有类似的代码
if(map.containsKey(key)) doSomethingWith(map.get(key))
到处都是?然后你应该简单地检查
map.get(key)
是否返回 null ,仅此而已。
顺便说一句,HashMap 不会因缺少键而抛出异常,而是返回 null。唯一需要 containsKey
的情况是当您存储空值时,以区分空值和缺失值,但这通常被认为是不好的做法。
为了清楚起见,请使用
containsKey()
。它速度很快,并且保持代码干净和可读。 HashMap
的全部要点是键查找速度很快,只需确保 hashCode()
和 equals()
正确实现即可。
您还可以使用 computeIfAbsent()
类中的
HashMap
方法。
在以下示例中,
map
存储应用于密钥(银行帐户名称)的交易(整数)列表。要将 100
和 200
的 2 个交易添加到 checking_account
,您可以编写:
HashMap<String, ArrayList<Integer>> map = new HashMap<>();
map.computeIfAbsent("checking_account", key -> new ArrayList<>())
.add(100)
.add(200);
这样你就不必检查密钥
checking_account
是否存在。
computeIfAbsent()
将返回键的值。 真的很优雅! 👍
if(map.get(key) != null || (map.get(key) == null && map.containsKey(key)))
从java 1.8开始,你可以简单地使用:
var item = mapObject.getOrDefault(key, null);
if(item != null)
我通常使用这个习语
Object value = map.get(key);
if (value == null) {
value = createValue(key);
map.put(key, value);
}
这意味着如果钥匙丢失,你只能点击地图两次
Jon Skeet 的答案以有效的方式很好地解决了两种情况(使用
null
值映射而不是 null
值)。关于数量条目和效率问题,我想补充一下。
我有一个 HashMap,其中有 1000 个条目,我正在考虑改进 效率。如果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
,则可能会发生一些碰撞,但Map
中的对象数量从一千到数千个,您应该拥有很少的数据,因为 String.hashCode()
方法提供了良好的分布。如果您使用自定义类型,请正确覆盖
hashCode()
和 equals()
,并确保总体上 hashCode()
提供公平的分配。Java Effective
第9项提到的。如果地图不包含我们要查找的键,map.get(key)将返回null。下面的代码块将返回 Key : null
Map<String,String> map = new HashMap<>();
map.put("a","A");
System.out.println("Key : " +map.get("b"));