Me gustaría usar una cadena que no distinga entre mayúsculas y minúsculas como una clave HashMap por las siguientes razones.
- Durante la inicialización, mi programa crea HashMap con String definido por el usuario
- Mientras procesaba un evento (tráfico de red en mi caso), podría recibir String en un caso diferente, pero debería poder ubicarlo
<key, value>
desde HashMap ignorando el caso que recibí del tráfico.
He seguido este enfoque
CaseInsensitiveString.java
public final class CaseInsensitiveString {
private String s;
public CaseInsensitiveString(String s) {
if (s == null)
throw new NullPointerException();
this.s = s;
}
public boolean equals(Object o) {
return o instanceof CaseInsensitiveString &&
((CaseInsensitiveString)o).s.equalsIgnoreCase(s);
}
private volatile int hashCode = 0;
public int hashCode() {
if (hashCode == 0)
hashCode = s.toUpperCase().hashCode();
return hashCode;
}
public String toString() {
return s;
}
}
LookupCode.java
node = nodeMap.get(new CaseInsensitiveString(stringFromEvent.toString()));
Debido a esto, estoy creando un nuevo objeto de CaseInsensitiveString para cada evento. Por lo tanto, podría afectar el rendimiento.
¿Hay alguna otra forma de resolver este problema?
Respuestas:
Eso es realmente todo lo que necesitas.
fuente
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
<K extends String>
ya queString
es final:public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Como sugirió Guido García en su respuesta aquí :
O
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
fuente
containsKey()
yremove()
debería ser anulado de la misma manera queget()
.HashMap.putAll()
utiliza la implementaciónput()
, por lo que no debería ser un problema, siempre que la implementación de HashMap permanezca igual. ;) también laget()
firma del método toma unObject
argumento, no unString
. También el código no comprueba una clave nula:super.get(key == null ? null : key.toString().toLowercase());
HashMap(<? extends String, ? extends String> anotherMap)
, entonces no debe llamar a la súper implementación del mismo constructor, ya que esa operación no garantizará que sus claves sean minúsculas. podrías usar: en susuper(anotherMap.size()); putAll(anotherMap);
lugar.CaseInsensitiveMap<String, Integer>
)Un enfoque es crear una subclase personalizada de la
AbstractHashedMap
clase Apache Commons , anulando los métodoshash
yisEqualKeys
para realizar el hash de mayúsculas y minúsculas y la comparación de claves. (Nota: nunca lo he intentado yo mismo ...)Esto evita la sobrecarga de crear nuevos objetos cada vez que necesite hacer una búsqueda o actualización de un mapa. Y las
Map
operaciones comunes deberían O (1) ... como una normalHashMap
.Y si está preparado para aceptar las opciones de implementación que han hecho, Apache Commons
CaseInsensitiveMap
hace el trabajo de personalización / especializaciónAbstractHashedMap
para usted.Pero si O (logN)
get
y lasput
operaciones son aceptables, unTreeMap
comparador de cadenas con mayúsculas y minúsculas es una opción; por ejemplo, usandoString.CASE_INSENSITIVE_ORDER
.Y si no te importa crear un nuevo objeto String temporal cada vez que haces un
put
oget
, entonces la respuesta de Vishal está bien. (Sin embargo, noto que no conservaría el estuche original de las llaves si hiciera eso ...)fuente
Subclase
HashMap
y cree una versión que minimice la claveput
yget
(y probablemente los otros métodos orientados a la clave).O componga a
HashMap
en la nueva clase y delegue todo al mapa, pero traduzca las claves.Si necesita conservar la clave original, puede mantener mapas duales o almacenar la clave original junto con el valor.
fuente
HashMap
, así que eso es lo que elegí :) Oh, te refieres al de Commons; Veo. Supongo, siempre y cuando no lo necesites genérico (¿o finalmente tienen genéricos ahora?)Se me ocurren dos opciones:
s.toUpperCase().hashCode();
como la clave delMap
.TreeMap<String>
con una costumbreComparator
que ignore el caso.De lo contrario, si prefiere su solución, en lugar de definir un nuevo tipo de Cadena, preferiría implementar un nuevo Mapa con la funcionalidad de insensibilidad de mayúsculas y minúsculas requerida.
fuente
¿No sería mejor "envolver" la Cadena para memorizar el hashCode. En la clase de cadena normal, hashCode () es O (N) la primera vez y luego es O (1) ya que se mantiene para su uso futuro.
Esto le permitiría usar cualquier implementación de Hashtable en Java y tener O (1) hasCode ().
fuente
Puede usar una HashingStrategy basada
Map
en Eclipse CollectionsNota: Soy colaborador de Eclipse Collections.
fuente
Basado en otras respuestas, existen básicamente dos enfoques: subclases
HashMap
o ajusteString
. El primero requiere un poco más de trabajo. De hecho, si desea hacerlo correctamente, debe anular casi todos los métodos (containsKey, entrySet, get, put, putAll and remove
).De todos modos, tiene un problema. Si desea evitar problemas futuros, debe especificar una
Locale
enString
caso de operaciones. Entonces crearías nuevos métodos (get(String, Locale)
, ...). Todo es más fácil y más claro envolviendo la Cadena:Y bueno, sobre sus preocupaciones sobre el rendimiento: la optimización prematura es la raíz de todo mal :)
fuente
Este es un adaptador para HashMaps que implementé para un proyecto reciente. Funciona de manera similar a lo que hace @SandyR, pero encapsula la lógica de conversión para que no convierta cadenas manualmente en un objeto contenedor.
Utilicé las funciones de Java 8 pero con algunos cambios, puedes adaptarlo a versiones anteriores. Lo probé para los escenarios más comunes, excepto las nuevas funciones de flujo Java 8.
Básicamente, envuelve un HashMap, dirige todas las funciones hacia él mientras convierte cadenas a / desde un objeto contenedor. Pero también tuve que adaptar KeySet y EntrySet porque reenvían algunas funciones al mapa en sí. Así que devuelvo dos nuevos conjuntos para claves y entradas que realmente envuelven el keySet original () y entrySet ().
Una nota: Java 8 ha cambiado la implementación del método putAll que no pude encontrar una manera fácil de anular. Por lo tanto, la implementación actual puede haber degradado el rendimiento, especialmente si usa putAll () para un conjunto de datos grande.
Avíseme si encuentra un error o tiene sugerencias para mejorar el código.
paquete webbit.collections;
fuente
Crear envoltorios o convertir la clave a minúsculas antes de buscar ambos crea nuevos objetos. Escribir su propia implementación de java.util.Map es la única forma de evitar esto. No es demasiado difícil, y la OMI lo vale. Encontré que la siguiente función hash funciona bastante bien, hasta unos cientos de claves.
fuente
¿Qué tal el uso de Java 8 streams?
fuente