java - para - manual de programacion android pdf
¿Cómo verificar una clave en un mapa independientemente del caso? (9)
Esta pregunta ya tiene una respuesta aquí:
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í.
Hay una clase CaseInsensitiveMap en Apache commons
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.