2010-09-02 18 views
228

¿Siempre es necesario verificar la existencia de claves en HashMap?Comprobación de existencia de clave en HashMap

Tengo un HashMap con unas 1000 entradas y estoy buscando mejorar la eficiencia. Si se accede al HashMap con mucha frecuencia, la comprobación de la existencia de la clave en cada acceso generará una gran sobrecarga. En cambio, si la clave no está presente y, por lo tanto, se produce una excepción, puedo detectar la excepción. (cuando sé que esto sucederá raramente). Esto reducirá los accesos al HashMap a la mitad.

Puede que esta no sea una buena práctica de programación, pero me ayudará a reducir el número de accesos. ¿O me estoy perdiendo algo aquí?

[Update] No tengo valores nulos en HashMap.

+8

"por lo tanto y se produce una excepción" - ¿qué excepción? Esto no será de java.util.HashMap ... – serg10

Respuesta

379

¿Alguna vez almacenó un valor nulo? Si no es así, sólo puede hacer:

Foo value = map.get(key); 
if (value != null) { 
    ... 
} else { 
    // No such key 
} 

De lo contrario, podría acaba de comprobar la existencia si se obtiene un valor nulo devuelto:

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

por lo general uso el lenguaje

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

Esto significa que solo aparece en el mapa dos veces si falta la clave

14

¿Quiere decir que usted tiene un código como

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

por todo el lugar? Entonces simplemente debe verificar si map.get(key) devolvió nulo y eso es todo. Por cierto, HashMap no arroja excepciones para las claves que faltan, sino que devuelve nulo. El único caso en que se necesita containsKey es cuando está almacenando valores nulos, para distinguir entre un valor nulo y un valor perdido, pero esto generalmente se considera una mala práctica.

55

No obtendrá nada comprobando que la clave existe. Este es el código de 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; 
} 

Sólo comprobar si el valor de retorno para get() es diferente de null.

Este es el código fuente de HashMap.


Recursos:

+2

¿De qué sirve mostrar una implementación específica de estos métodos? – jarnbjo

+2

Explicar que en la mayoría de los casos, verificar que la clave exista tardará aproximadamente el mismo tiempo que obtener el valor.Por lo tanto, no optimizará nada para verificar que la clave realmente exista antes de obtener el valor. Sé que es una generalización, pero puede ayudar a comprender. –

+0

Un buen enlace es http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java (OpenJDK se deriva muy fuertemente del código de Sun) y parece que estoy equivocado. Estaba comparando la versión para Java5 con Java6; funcionan de manera diferente en esta área (pero ambos son correctos, al igual que los fragmentos que ha publicado). –

0
  1. Si la clase clave es su de asegurarse de que el hashCode() y equals () métodos implementado.
  2. Básicamente, el acceso a HashMap debe ser O (1) pero con la implementación incorrecta del método hashCode se ha convertido en O (n), porque el valor con la misma clave hash se almacenará como lista vinculada.
32

Mejor manera es utilizar el método containsKey de HashMap. Tomorrow soembody agregará null al mapa, debe diferenciar entre la clave y la clave tiene valor nulo.

+0

Sí. O subclase el HashMap para evitar almacenar 'null' en conjunto. – RobAu

+1

1+ para tipos primitivos, ya que no es necesario el valor del molde innecesario con esta respuesta –

+0

También es más fluido escribir .containsKey() que verificar nulo. Deberíamos preocuparnos más por ser fáciles de leer, lo que ahorra tiempo a los desarrolladores, que por una optimización menor, en la mayoría de los casos. Al menos no optimizar antes de que sea necesario. – Max

4

Simplemente use containsKey() para mayor claridad. Es rápido y mantiene el código limpio y legible. El objetivo de HashMap s es que la búsqueda de claves sea rápida, solo asegúrese de que hashCode() y equals() estén implementados correctamente.

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

The Jon Skeet answer direcciones así los dos escenarios (mapa con null valor y no null valor) de una manera eficiente.

Acerca de las entradas de números y la preocupación por la eficiencia, me gustaría agregar algo.

Tengo un HashMap con unas 1.000 entradas y estoy buscando mejorar la eficiencia. Si se accede al HashMap con mucha frecuencia, entonces comprobando la existencia de la clave en cada acceso dará lugar a una gran sobrecarga .

Un mapa con 1.000 entradas no es un mapa enorme.
Además de un mapa con 5.000 o 10.000 entradas.
Map están diseñados para realizar una recuperación rápida con tales dimensiones.

Ahora, se supone que hashCode() de las teclas del mapa proporciona una buena distribución.

Si puede usar un Integer como tipo de llave, hágalo.
Su método hashCode() es muy eficiente ya que las colisiones no son posibles para int valores únicos:

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; 
    } 
    ... 
} 

Si la llave, usted tiene que utilizar otro tipo incorporado como String, por ejemplo, que se utiliza a menudo en Map , es posible que tenga algunas colisiones, pero de 1 mil a algunos miles de objetos en el Map, debe tener muy pocos, ya que el método String.hashCode() proporciona una buena distribución.

Si utiliza un tipo personalizado, anular y hashCode()equals() correctamente y asegúrese de que hashCode() general proporciona una distribución justa.
Puede consultar el artículo 9 de Java Effective lo refiere.
Aquí hay un post que detalla el camino.

Cuestiones relacionadas