studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones java

java - para - manual de programacion android pdf



¿Cómo verificar una clave en un mapa independientemente del caso? (9)

Quiero saber si una clave en particular está presente en un HashMap, por lo que estoy usando el métodolave ​​(clave). Pero distingue entre mayúsculas y minúsculas, es decir, no devuelve verdadero si hay una clave con Nombre y estoy buscando un nombre. Entonces, ¿hay alguna forma en que pueda saberlo sin molestar el caso de la llave?

Gracias


En un intento de presentar una respuesta que coincida con el requisito de su pregunta "sin molestar al caso de la clave" ...

Esta respuesta puede ser tediosa si agrega en su mapa en muchos, muchos lugares. En mi ejemplo, solo sucede cuando un usuario crea un nuevo personaje (en mi juego). Aquí es cómo manejé esto:

boolean caseInsensitiveMatch = false; for (Map.Entry<String, Character> entry : MyServer.allCharacterMap.entrySet()) { if (entry.getKey().toLowerCase().equals(charNameToCreate.toLowerCase())){ caseInsensitiveMatch = true; break; } }

Por supuesto, esto requiere recorrer mi gran Concashent HashMap, pero funciona para mí.



La forma más fácil es doblar las llaves usted mismo al insertarlas y buscarlas. Es decir

map.put(key.toLowerCase(), value);

y

map.get(key.toLowerCase());

Podría crear una subclase, por ejemplo, HashMap, para obtener su propia clase con estos, si desea que esto se haga automáticamente.


No con los mapas convencionales.

"abc" es una cadena distinta de "ABC", sus códigos de hash son diferentes y sus métodos equals () se devolverán falsos entre sí.

La solución más simple es simplemente convertir todas las entradas a mayúsculas (o minúsculas) antes de insertar / verificar. Incluso podría escribir su propio contenedor de Map que haría esto para asegurar la consistencia.

Si desea mantener el caso de la clave como se proporciona, pero con una comparación que no distingue entre mayúsculas y minúsculas, puede utilizar un TreeMap y proporcionar su propio Comparador que lo comparará sin tener en cuenta las mayúsculas. Sin embargo, piense detenidamente antes de seguir esta ruta, ya que terminará con algunas inconsistencias irreconciliables: si alguien llama map.put("abc", 1) luego map.put("ABC", 2) , ¿qué caso es la clave almacenada? en el mapa? ¿Puedes incluso hacer que esto tenga sentido? ¿Se siente cómodo con el hecho de que si alguien ajusta su mapa en un estándar, por ejemplo, HashMap , perderá la funcionalidad? ¿O si, de todos modos, si alguien está iterando a través de su conjunto de teclas, y su comprobación rápida "contenida" al utilizar equals() obtendrá resultados inconsistentes? Habrá muchos otros casos como este también. Tenga en cuenta que está violando el contrato de Map al hacer esto (ya que la igualdad de claves se define en términos del método equals () en las claves ), por lo que en realidad no es viable en ningún sentido.

Mantener un mapa en mayúsculas estricto es mucho más fácil de trabajar y mantener, y tiene la ventaja de ser realmente una implementación de mapas legal.


Para preservar los invariantes del Map , puede crear sus propias claves. Implementa hashCode / equals sensibles y listo:

final class CaseInsensitive { private final String s; private final Local lc; public CaseInsensitive (String s, Locale lc) { if (lc == null) throw new NullPointerException(); this.s = s; this.lc = lc; } private s(){ return s == null ? null : s.toUpperCase(lc); } @Override public int hashCode(){ String u = s(); return (u == null) ? 0 : u.hashCode(); } @Override public boolean equals(Object o){ if (!getClass().isInstance(o)) return false; String ts = s(), os = ((CaseInsensitive)other).s(); if (ts == null) return os == null; return ts.equals(os); } } // Usage: Map<CaseInsensitive, Integer> map = ...; map.put(new CaseInsensitive("hax", Locale.ROOT), 1337); assert map.get(new CaseInsensitive("HAX", Locale.ROOT) == 1337;

Nota: no todos en el mundo están de acuerdo con lo que está en mayúsculas: un ejemplo famoso es que la versión en mayúsculas de "i" en turco es "İ", no "I".


Puede usar un TreeMap con un Comparator personalizado que no distingue entre mayúsculas y minúsculas (que utiliza String.compareToIgnoreCase() )

Por ejemplo:

Map<String, Something> map = new TreeMap<String, Something>(CaseInsensitiveComparator.INSTANCE); class CaseInsensitiveComparator implements Comparator<String> { public static final CaseInsensitiveComparator INSTANCE = new CaseInsensitiveComparator(); public int compare(String first, String second) { // some null checks return first.compareToIgnoreCase(second); } }

Actualización: parece que String ya ha definido este Comparator como una constante.


Use un TreeMap que se construye con la String#CASE_INSENSITIVE_ORDER .

Map<String, String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); map.put("FOO", "FOO"); System.out.println(map.get("foo")); // FOO System.out.println(map.get("Foo")); // FOO System.out.println(map.get("FOO")); // FOO


crea tu propio contenedor de clase de cadena, implementa iguales y hashcode, usa esto como la clave en el hashmap:

class MyStringKey { private String string; public String getString() { return string; } public void setString(String string) { this.string = string; } public boolean equals(Object o) { return o instanceof MyStringKey && this.equalsIgnoreCase(((MyStringKey)o).getString()); } public boolean hashCode() { return string.toLowerCase().hashcode(); //STRING and string may not have same hashcode } }


Map usa equals y hashCode para probar la igualdad de claves, y no puedes sobrescribir estos para String . Lo que podría hacer es definir su propia clase de clave que contenga un valor de cadena, pero que implemente equals y hashCode de una manera que no hashCode mayúsculas y minúsculas.