2010-09-02 63 views
228

检查HashMap中的键存在是否必要?HashMap中的键存在检查

我有一个HashMap说1000个条目,我期待提高效率。 如果HashMap被非常频繁地访问,那么在每次访问时检查密钥是否存在都会导致很大的开销。相反,如果钥匙不存在,并因此发生异常,我可以捕捉到异常。 (当我知道这很少会发生)。这将使对HashMap的访问减少一半。

这可能不是一个好的编程习惯,但它会帮助我减少访问次数。或者我在这里错过了什么?

[更新]我在HashMap中没有空值。

+8

“因此发生异常” - 什么异常?这不是来自java.util.HashMap ... – serg10 2010-09-02 12:06:49

回答

379

你曾经存储过空值吗?如果没有,你可以这样做:

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 
    } 
} 
0

我通常使用成语

Object value = map.get(key); 
if (value == null) { 
    value = createValue(key); 
    map.put(key, value); 
} 

这意味着如果你丢失了钥匙,你只能打两次地图

14

你的意思是,你有这样的代码

if(map.containsKey(key)) doSomethingWith(map.get(key))

所有的地方?那么你应该简单地检查map.get(key)是否返回null,就是这样。顺便说一下,HashMap不会为丢失的键引发异常,而是返回null。唯一需要containsKey的情况是当你存储空值时,区分空值和缺失值,但这通常被认为是不好的做法。

55

通过检查密钥是否存在不会获得任何结果。这是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源代码。


资源:

+2

显示这些方法的一个具体实现的重点是什么? – jarnbjo 2010-09-02 12:37:45

+2

为了解释在大多数情况下,检查密钥是否存在与获取值大致相同。因此,在获取值之前,它不会优化检查密钥的实际存在。我知道这是一个泛化,但它可以帮助理解。 – 2010-09-02 12:42:54

+0

一个很好的链接是http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java(OpenJDK非常强大的从Sun代码派生而来),似乎我错了。我正在比较Java5和Java6的版本;他们在这方面的工作方式不同(但都是正确的,因为您发布的片段)。 – 2010-09-02 15:02:41

0
  1. 如果重点班是你的确保的hashCode()和equals( ) 方法 实现。
  2. 基本上对HashMap的访问应该是O(1),但如果使用错误的hashCode方法实现,它将变成O(n),因为具有相同散列键的值将存储为链接列表。
32

更好的方法是使用HashMap的containsKey方法。明天soembody会将null添加到映射中,您应该区分键和空键值。

+0

是的。或者对HashMap进行子类化,以防止一起存储'null'。 – RobAu 2015-04-17 07:04:15

+1

1+对于基本类型,因为不需要使用此答案cast不需要使用 – 2016-09-23 09:53:09

+0

写入.containsKey()比检查null更为流利。在大多数情况下,我们应该更加担心易于阅读,从而节省开发人员的时间,而不是一些小的优化。在必要之前至少不要优化。 – Max 2017-04-24 17:56:53

4

为了清楚起见,仅使用containsKey()。它速度很快,并保持代码清洁和可读。 HashMap s的整点是密钥查找速度很快,只要确保hashCode()equals()正确实施。

3
if(map.get(key) != null || (map.get(key) == null && map.containsKey(key))) 
0

The Jon Skeet answer地址以及这两种情况(地图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使用,您可能会遇到一些碰撞,但在Map中有1000个到几千个对象,因此String.hashCode()方法提供了良好的分布,所以您应该只有很少的几个对象。

如果您使用自定义类型,请正确覆盖hashCode()equals(),并确保整体hashCode()提供公平分配。
您可能会参考Java Effective的第9项提到它。
这是一个post详细的方式。