Pregunta

Esta pregunta se le pide por extraño HashMap.put() comportamiento

Creo que entiendo por qué Map<K,V>.put toma un K pero Map<K,V>.get toma un Object, parece no hacerlo va a romper demasiado el código existente.

Ahora entramos en una muy propenso a errores escenario:

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L,"Five"); // compiler barfs on m.put(5, "Five")
m.contains(5); // no complains from compiler, but returns false

No podía haber sido resuelto por devuelve true si el Long valor dentro de int rango y los valores son iguales?

¿Fue útil?

Solución

Aquí está la fuente de Long.java

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

I.e. tiene que ser un tipo largo para ser igual. Creo que la diferencia clave entre:

long l = 42L
int i = 42;
l == i

y su ejemplo anterior es que con las primitivas puede ocurrir un ensanchamiento implícito del valor int, sin embargo, con los tipos de objetos no hay reglas para convertir implícitamente de Entero a Largo.

Consulte también Java Puzzlers , tiene muchos ejemplos similares a este.

Otros consejos

Generalmente hablando, aunque no es estrictamente expresado en el contrato para equals(), los objetos no se consideran a sí mismos igual a otro objeto que no es de exactamente la misma clase (incluso si es una subclase).Considerar la propiedad simétrica: si una.es igual a(b) es cierto, entonces b.es igual a(a) también debe ser cierto.

Vamos a tener dos objetos, foo de clase Super, y bar de clase Sub, que se extiende Super.Ahora considere la implementación de equals() en el Super, específicamente cuando se llama como la foo.equals(bar).Foo sólo se sabe que la barra es inflexible como una Object, por lo que para obtener una comparación exacta que necesita para comprobar que es una instancia de Super y si no devuelve falso.Es, por lo que esta parte está muy bien.Ahora compara todos los campos de instancia, etc.(o lo que sea la comparación real de implementación es), y los encuentra a ellos igual.Tan lejos, tan bueno.

Sin embargo, por el contrato sólo puede devolver true si se sabe que la barra.es igual a(foo) se va a devolver cierto.Desde la barra puede ser de cualquier subclase de Super, no está claro si el equals() método va a ser reemplazado (y si probablemente será).Por lo tanto, para estar seguro de que su aplicación es correcta, usted necesita para escribir de forma simétrica y asegurarse de que los dos objetos de la misma clase.

Más fundamentalmente, los objetos de clases diferentes no puede ser considerado igual - ya que en este caso, sólo uno de ellos puede ser insertado en un HashSet<Sub>, por ejemplo.

Sí, pero todo se reduce a la comparación de algoritmo y en qué medida a tomar las conversiones.Por ejemplo, ¿qué quieres que suceda cuando se trate de m.Contains("5")?O si se le pasa un array con 5 como el primer elemento?Simplemente hablando, parece estar conectado "si los tipos son diferentes, las claves son diferentes".

A continuación, tomar una colección con un object como la clave.¿Qué desea que suceda si usted put un 5L, a continuación, tratar de conseguir 5, "5", ...?Lo que si put un 5L y un 5 y un "5" y desea comprobar un 5F?

Ya que es una colección genérica (o de plantilla, o lo que usted quiera llamar), se tendría que buscar y hacer algo especial comparando para ciertos tipos de valor.Si K es int a continuación, compruebe si el objeto pasado es long, short, float, double, ..., a continuación, convertir y comparar.Si K es float a continuación, compruebe si el objeto pasado es ...

Usted consigue el punto.

Otra aplicación podría haber sido para lanzar una excepción si los tipos no coinciden, sin embargo, y a menudo me gustaría que lo hizo.

Su pregunta parece razonable a primera vista, pero sería una violación de las convenciones generales para equals (), si no su contrato, devolver verdadero para dos tipos diferentes.

Parte del diseño del lenguaje Java fue para que los Objetos nunca se conviertan implícitamente a otros tipos, a diferencia de C ++. Esto fue parte de hacer de Java un lenguaje pequeño y simple. Una parte razonable de la complejidad de C ++ proviene de las conversiones implícitas y sus interacciones con otras características.

Además, Java tiene una dicotomía nítida y visible entre primitivas y objetos. Esto es diferente de otros idiomas donde esta diferencia se oculta debajo de las cubiertas como una optimización. Esto significa que no puede esperar que Long e Integer actúen como long e int.

El código de la biblioteca se puede escribir para ocultar estas diferencias, pero eso realmente puede hacer daño al hacer que el entorno de programación sea menos coherente.

Entonces su código debe ser ...

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L, "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(5L)); // true

Se está olvidando de que Java está autoboxing su código, por lo que el código anterior sería equivalente a

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(new Long(5L), "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(new Long(5))); // true
System.out.println(m.containsKey(new Long(5L))); // true

Entonces, una parte de su problema es el autoboxing. La otra parte es que tiene diferentes tipos, como han indicado otros carteles.

Las otras respuestas explican adecuadamente por qué falla, pero ninguna de ellas aborda cómo escribir código que sea menos propenso a errores en torno a este problema. Tener que recordar agregar conversiones de tipo (sin ayuda del compilador), las primitivas de sufijo con L, etc., simplemente no es aceptable en mi humilde opinión.

Recomiendo usar la biblioteca de colecciones GNU trove cuando tenga primitivas (y en muchos otros casos). Por ejemplo, hay un TLongLongHashMap que almacena cosas internamente como largos primitivos. Como resultado, nunca termina con el boxeo / unboxing, y nunca termina con comportamientos inesperados:

TLongLongHashMap map = new TLongLongHashMap();
map.put(1L, 45L);
map.containsKey(1); // returns true, 1 gets promoted to long from int by compiler
int x = map.get(1); // Helpful compiler error. x is not a long
int x = (int)map.get(1); // OK. cast reassures compiler that you know
long x = map.get(1); // Better.

y así sucesivamente. No es necesario obtener el tipo correcto, y el compilador le da un error (que puede corregir o anular) si hace algo tonto (intente almacenar un largo en un int).

Las reglas de transmisión automática significan que las comparaciones también funcionan correctamente:

if(map.get(1) == 45) // 1 promoted to long, 45 promoted to long...all is well

Como beneficio adicional, el rendimiento de la sobrecarga de memoria y el tiempo de ejecución es mucho mejor.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top