java - significa - Comparador no sensible a mayúsculas y minúsculas rompe mi TreeMap
para que se utiliza el equalsignorecase (4)
Debe asegurarse de que la igualdad de los elementos de ese mapa sea consistente con el comparador. Citando el comentario de la clase:
Tenga en cuenta que la ordenación mantenida por un mapa de árbol, como cualquier mapa ordenado, y si se proporciona o no un comparador explícito, debe ser consistente con iguales si este mapa ordenado debe implementar correctamente la interfaz.
Un Comparator
que utilicé en mi TreeMap
rompió el comportamiento que pretendía para ese TreeMap
. Mira el siguiente código:
TreeMap<String, String> treeMap = new TreeMap<>(new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.toLowerCase().compareTo(o2.toLowerCase());
}
});
treeMap.put("abc", "Element1");
treeMap.put("ABC", "Element2");
Lo que creo que he hecho es que he creado un mapa que está ordenado por sus claves, sin distinción de mayúsculas y minúsculas. Los dos elementos distintos tienen claves no iguales ( abc
y ABC
) cuya comparación devolverá 0
. Esperaba un orden aleatorio de los dos elementos. Sin embargo, el comando:
System.out.println("treeMap: " + treeMap);
resultó en:
treeMap: {abc=Element2}
La clave abc
se ha reasignado al valor Element2
!
¿Alguien puede explicar cómo podría suceder esto y si es un comportamiento válido y documentado de TreeMap
?
El Comparator
que pasa a un TreeMap
determina no solo el orden de las claves dentro del Map
, sino que también determina si dos claves se consideran idénticas (se consideran idénticas cuando compare()
devuelve 0
).
Por lo tanto, en su TreeMap
, "abc" y "ABC" se consideran claves idénticas. Map
no permiten claves idénticas, por lo que el segundo valor Element2
sobrescribe el primer valor Element1
.
La respuesta aceptada es técnicamente correcta, pero pierde la solución idiomática al problema.
Debería usar el comparador String.CASE_INSENSITIVE_ORDER
estático que se proporciona, o al menos usar String.compareToIgnoreCase()
dentro de la suya para considerar qué es .equal()
.
Para comparaciones sensibles al entorno local, debe usar algo de java.text.Collator
Ocurre porque TreeMap
considera elementos iguales si a.compareTo(b) == 0
. Está documentado en el JavaDoc para TreeMap (énfasis mío):
Tenga en cuenta que la ordenación mantenida por un mapa de árbol, como cualquier mapa ordenado, y si se proporciona o no un comparador explícito, debe ser consistente con
equals
si este mapa ordenado implementa correctamente la interfaz del Mapa. (ConsulteComparable
oComparator
para una definición precisa de consistente conequals
). Esto se debe a que la interfaz del Mapa se define en términos de la operación deequals
, pero un mapa clasificado realiza todas las comparaciones de claves usando sucompareTo
(ocompare
), por lo que dos Las claves que se consideran iguales por este método son , desde el punto de vista del mapa ordenado, iguales . El comportamiento de un mapa ordenado está bien definido incluso si su orden es inconsistente con losequals
; simplemente no cumple con el contrato general de la interfaz del Mapa.
Tu comparador no es consistente con iguales.
Si desea mantener los elementos de caso que no son iguales pero sí que son ignorados, ponga un segundo nivel de verificación en su comparador, para usar el ordenamiento que distingue entre mayúsculas y minúsculas:
public int compare(String o1, String o2) {
int cmp = o1.toLowerCase().compareTo(o2.toLowerCase());
if (cmp != 0) return cmp;
return o1.compareTo(o2);
}