Cadena insensible a mayúsculas y minúsculas como clave HashMap

178

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?

rs
fuente
3
[¿Hay una buena manera de tener un Map <String,?> Get and put ignore case?] [1] [1]: stackoverflow.com/questions/212562/…
Beau Grantham
He comentado los siguientes problemas, pero están por debajo del umbral, por lo que es posible que las personas no los vean. Cuidado con subclasificar HashMap. JDK8 ha cambiado la implementación y ahora debe anular putAll (al menos) para que esas sugerencias funcionen.
Steve N
Esto debería funcionar bien. Puede usar un peso mosca para deshacerse de la nueva instancia de objeto.
topkara

Respuestas:

331
Map<String, String> nodeMap = 
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

Eso es realmente todo lo que necesitas.

Roel Spilker
fuente
66
Este es, de lejos, el más simple y también conserva el caso de las teclas al recorrerlas.
Ralf
¡Esto es hermoso! Esta fue la pieza final del rompecabezas para crear una estructura ordenada en ColdFusion que conserva la capacidad de usar la notación de puntos. En lugar de var struct = {} o var struct = structnew () puede usar var struct = createObject ('java', 'java.util.TreeMap'). Init (createObject ('java', 'java.lang.String' ) .CASE_INSENSITIVE_ORDER); FUGLY, pero funciona;)
Eric Fuller
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
favor
55
No es necesario <K extends String>ya que Stringes final: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker
19
Tenga en cuenta que TreeMap no es un tiempo constante para las operaciones básicas. No es un problema para la mayoría de las aplicaciones, pero vale la pena tenerlo en cuenta. De JavaDoc: "Esta implementación proporciona un costo de tiempo de registro (n) garantizado para las operaciones contiene clave, obtener, poner y quitar. Los algoritmos son adaptaciones de aquellos en Cormen, Leiserson y la Introducción a los algoritmos de Rivest".
James Schek
57

Como sugirió Guido García en su respuesta aquí :

import java.util.HashMap;

public class CaseInsensitiveMap extends HashMap<String, String> {

    @Override
    public String put(String key, String value) {
       return super.put(key.toLowerCase(), value);
    }

    // not @Override because that would require the key parameter to be of type Object
    public String get(String key) {
       return super.get(key.toLowerCase());
    }
}

O

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html

Vishal
fuente
28
¿Qué tal contiene, putAll, etc.?
Assylias
14
Esto no funciona en algunos idiomas, como el turco. Google "La prueba de Turquía"
Hugo
55
@assylias: verdadero, containsKey()y remove()debería ser anulado de la misma manera que get(). HashMap.putAll()utiliza la implementación put(), por lo que no debería ser un problema, siempre que la implementación de HashMap permanezca igual. ;) también la get()firma del método toma un Objectargumento, no un String. También el código no comprueba una clave nula:super.get(key == null ? null : key.toString().toLowercase());
sfera
tenga en cuenta que si necesita el constructor de copia 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 su super(anotherMap.size()); putAll(anotherMap);lugar.
sfera
¿Qué sucede si desea que los valores del mapa no sean cadenas? (es decir CaseInsensitiveMap<String, Integer>)
Adam Parkin
16

Un enfoque es crear una subclase personalizada de la AbstractHashedMapclase Apache Commons , anulando los métodos hashy isEqualKeyspara 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 Mapoperaciones comunes deberían O (1) ... como una normal HashMap.

Y si está preparado para aceptar las opciones de implementación que han hecho, Apache Commons CaseInsensitiveMaphace el trabajo de personalización / especialización AbstractHashedMappara usted.


Pero si O (logN) gety las putoperaciones son aceptables, un TreeMapcomparador de cadenas con mayúsculas y minúsculas es una opción; por ejemplo, usando String.CASE_INSENSITIVE_ORDER.

Y si no te importa crear un nuevo objeto String temporal cada vez que haces un puto get, entonces la respuesta de Vishal está bien. (Sin embargo, noto que no conservaría el estuche original de las llaves si hiciera eso ...)

Stephen C
fuente
6

Subclase HashMapy cree una versión que minimice la clave puty get(y probablemente los otros métodos orientados a la clave).

O componga a HashMapen 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.

Dave Newton
fuente
¿Te refieres a hacer un String.toLowerCase () durante la búsqueda?
rs
@ user710178 No solo durante la búsqueda, sino también durante el almacenamiento.
Dave Newton el
@ user710178 Oh, cierto, como señala esa otra respuesta, esto ya existe, si no le importa una dependencia adicional.
Dave Newton el
@StephenC Si satisface tus necesidades, seguro; OP especificó un 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?)
Dave Newton el
1
Para JDK 8 y superior, también deberá (al menos) anular putAll a medida que la implementación ha cambiado.
Steve N
4

Se me ocurren dos opciones:

  1. Puede usar directamente el s.toUpperCase().hashCode();como la clave del Map.
  2. Puede usar un TreeMap<String>con una costumbre Comparatorque 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.

Gabriel Belingueres
fuente
3

¿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.

public class HashWrap {
    private final String value;
    private final int hash;

    public String get() {
        return value;
    }

    public HashWrap(String value) {
        this.value = value;
        String lc = value.toLowerCase();
        this.hash = lc.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o instanceof HashWrap) {
            HashWrap that = (HashWrap) o;
            return value.equalsIgnoreCase(that.value);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return this.hash;
    }

    //might want to implement compare too if you want to use with SortedMaps/Sets.
}

Esto le permitiría usar cualquier implementación de Hashtable en Java y tener O (1) hasCode ().

le-doude
fuente
3

Puede usar una HashingStrategy basada Mapen Eclipse Collections

HashingStrategy<String> hashingStrategy =
    HashingStrategies.fromFunction(String::toUpperCase);
MutableMap<String, String> node = HashingStrategyMaps.mutable.of(hashingStrategy);

Nota: Soy colaborador de Eclipse Collections.

Nikhil Nanivadekar
fuente
2

Basado en otras respuestas, existen básicamente dos enfoques: subclases HashMapo ajuste String. 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 Localeen Stringcaso de operaciones. Entonces crearías nuevos métodos ( get(String, Locale), ...). Todo es más fácil y más claro envolviendo la Cadena:

public final class CaseInsensitiveString {

    private final String s;

    public CaseInsensitiveString(String s, Locale locale) {
        this.s = s.toUpperCase(locale);
    }

    // equals, hashCode & toString, no need for memoizing hashCode
}

Y bueno, sobre sus preocupaciones sobre el rendimiento: la optimización prematura es la raíz de todo mal :)

sinuhepop
fuente
2
"Y bueno, sobre sus preocupaciones sobre el rendimiento: la optimización prematura es la raíz de todo mal :)" - Por el contrario, usar eso como una excusa para escribir siempre código ineficiente es lo que es malo.
Gordon
1
En realidad @Gordon, ambos son igualmente malos, dependiendo del contexto. La etiqueta "mal" es un signo de pensamiento en blanco y negro, como "mejores prácticas" y varias otras frases inútiles que muchas personas de TI tienden a usar. Lo mejor es evitarlo por completo.
Stephen C
Descubrí que decirle a la gente que no está siguiendo las "mejores prácticas" tiende a producir menos excavaciones que decirles que tienen malas prácticas.
Gordon
0

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;

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;


public class CaseInsensitiveMapAdapter<T> implements Map<String,T>
{
    private Map<CaseInsensitiveMapKey,T> map;
    private KeySet keySet;
    private EntrySet entrySet;


    public CaseInsensitiveMapAdapter()
    {
    }

    public CaseInsensitiveMapAdapter(Map<String, T> map)
    {
        this.map = getMapImplementation();
        this.putAll(map);
    }

    @Override
    public int size()
    {
        return getMap().size();
    }

    @Override
    public boolean isEmpty()
    {
        return getMap().isEmpty();
    }

    @Override
    public boolean containsKey(Object key)
    {
        return getMap().containsKey(lookupKey(key));
    }

    @Override
    public boolean containsValue(Object value)
    {
        return getMap().containsValue(value);
    }

    @Override
    public T get(Object key)
    {
        return getMap().get(lookupKey(key));
    }

    @Override
    public T put(String key, T value)
    {
        return getMap().put(lookupKey(key), value);
    }

    @Override
    public T remove(Object key)
    {
        return getMap().remove(lookupKey(key));
    }

    /***
     * I completely ignore Java 8 implementation and put one by one.This will be slower.
     */
    @Override
    public void putAll(Map<? extends String, ? extends T> m)
    {
        for (String key : m.keySet()) {
            getMap().put(lookupKey(key),m.get(key));
        }
    }

    @Override
    public void clear()
    {
        getMap().clear();
    }

    @Override
    public Set<String> keySet()
    {
        if (keySet == null)
            keySet = new KeySet(getMap().keySet());
        return keySet;
    }

    @Override
    public Collection<T> values()
    {
        return getMap().values();
    }

    @Override
    public Set<Entry<String, T>> entrySet()
    {
        if (entrySet == null)
            entrySet = new EntrySet(getMap().entrySet());
        return entrySet;
    }

    @Override
    public boolean equals(Object o)
    {
        return getMap().equals(o);
    }

    @Override
    public int hashCode()
    {
        return getMap().hashCode();
    }

    @Override
    public T getOrDefault(Object key, T defaultValue)
    {
        return getMap().getOrDefault(lookupKey(key), defaultValue);
    }

    @Override
    public void forEach(final BiConsumer<? super String, ? super T> action)
    {
        getMap().forEach(new BiConsumer<CaseInsensitiveMapKey, T>()
        {
            @Override
            public void accept(CaseInsensitiveMapKey lookupKey, T t)
            {
                action.accept(lookupKey.key,t);
            }
        });
    }

    @Override
    public void replaceAll(final BiFunction<? super String, ? super T, ? extends T> function)
    {
        getMap().replaceAll(new BiFunction<CaseInsensitiveMapKey, T, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey, T t)
            {
                return function.apply(lookupKey.key,t);
            }
        });
    }

    @Override
    public T putIfAbsent(String key, T value)
    {
        return getMap().putIfAbsent(lookupKey(key), value);
    }

    @Override
    public boolean remove(Object key, Object value)
    {
        return getMap().remove(lookupKey(key), value);
    }

    @Override
    public boolean replace(String key, T oldValue, T newValue)
    {
        return getMap().replace(lookupKey(key), oldValue, newValue);
    }

    @Override
    public T replace(String key, T value)
    {
        return getMap().replace(lookupKey(key), value);
    }

    @Override
    public T computeIfAbsent(String key, final Function<? super String, ? extends T> mappingFunction)
    {
        return getMap().computeIfAbsent(lookupKey(key), new Function<CaseInsensitiveMapKey, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey)
            {
                return mappingFunction.apply(lookupKey.key);
            }
        });
    }

    @Override
    public T computeIfPresent(String key, final BiFunction<? super String, ? super T, ? extends T> remappingFunction)
    {
        return getMap().computeIfPresent(lookupKey(key), new BiFunction<CaseInsensitiveMapKey, T, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey, T t)
            {
                return remappingFunction.apply(lookupKey.key, t);
            }
        });
    }

    @Override
    public T compute(String key, final BiFunction<? super String, ? super T, ? extends T> remappingFunction)
    {
        return getMap().compute(lookupKey(key), new BiFunction<CaseInsensitiveMapKey, T, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey, T t)
            {
                return remappingFunction.apply(lookupKey.key,t);
            }
        });
    }

    @Override
    public T merge(String key, T value, BiFunction<? super T, ? super T, ? extends T> remappingFunction)
    {
        return getMap().merge(lookupKey(key), value, remappingFunction);
    }

    protected  Map<CaseInsensitiveMapKey,T> getMapImplementation() {
        return new HashMap<>();
    }

    private Map<CaseInsensitiveMapKey,T> getMap() {
        if (map == null)
            map = getMapImplementation();
        return map;
    }

    private CaseInsensitiveMapKey lookupKey(Object key)
    {
        return new CaseInsensitiveMapKey((String)key);
    }

    public class CaseInsensitiveMapKey {
        private String key;
        private String lookupKey;

        public CaseInsensitiveMapKey(String key)
        {
            this.key = key;
            this.lookupKey = key.toUpperCase();
        }

        @Override
        public boolean equals(Object o)
        {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            CaseInsensitiveMapKey that = (CaseInsensitiveMapKey) o;

            return lookupKey.equals(that.lookupKey);

        }

        @Override
        public int hashCode()
        {
            return lookupKey.hashCode();
        }
    }

    private class KeySet implements Set<String> {

        private Set<CaseInsensitiveMapKey> wrapped;

        public KeySet(Set<CaseInsensitiveMapKey> wrapped)
        {
            this.wrapped = wrapped;
        }


        private List<String> keyList() {
            return stream().collect(Collectors.toList());
        }

        private Collection<CaseInsensitiveMapKey> mapCollection(Collection<?> c) {
            return c.stream().map(it -> lookupKey(it)).collect(Collectors.toList());
        }

        @Override
        public int size()
        {
            return wrapped.size();
        }

        @Override
        public boolean isEmpty()
        {
            return wrapped.isEmpty();
        }

        @Override
        public boolean contains(Object o)
        {
            return wrapped.contains(lookupKey(o));
        }

        @Override
        public Iterator<String> iterator()
        {
            return keyList().iterator();
        }

        @Override
        public Object[] toArray()
        {
            return keyList().toArray();
        }

        @Override
        public <T> T[] toArray(T[] a)
        {
            return keyList().toArray(a);
        }

        @Override
        public boolean add(String s)
        {
            return wrapped.add(lookupKey(s));
        }

        @Override
        public boolean remove(Object o)
        {
            return wrapped.remove(lookupKey(o));
        }

        @Override
        public boolean containsAll(Collection<?> c)
        {
            return keyList().containsAll(c);
        }

        @Override
        public boolean addAll(Collection<? extends String> c)
        {
            return wrapped.addAll(mapCollection(c));
        }

        @Override
        public boolean retainAll(Collection<?> c)
        {
            return wrapped.retainAll(mapCollection(c));
        }

        @Override
        public boolean removeAll(Collection<?> c)
        {
            return wrapped.removeAll(mapCollection(c));
        }

        @Override
        public void clear()
        {
            wrapped.clear();
        }

        @Override
        public boolean equals(Object o)
        {
            return wrapped.equals(lookupKey(o));
        }

        @Override
        public int hashCode()
        {
            return wrapped.hashCode();
        }

        @Override
        public Spliterator<String> spliterator()
        {
            return keyList().spliterator();
        }

        @Override
        public boolean removeIf(Predicate<? super String> filter)
        {
            return wrapped.removeIf(new Predicate<CaseInsensitiveMapKey>()
            {
                @Override
                public boolean test(CaseInsensitiveMapKey lookupKey)
                {
                    return filter.test(lookupKey.key);
                }
            });
        }

        @Override
        public Stream<String> stream()
        {
            return wrapped.stream().map(it -> it.key);
        }

        @Override
        public Stream<String> parallelStream()
        {
            return wrapped.stream().map(it -> it.key).parallel();
        }

        @Override
        public void forEach(Consumer<? super String> action)
        {
            wrapped.forEach(new Consumer<CaseInsensitiveMapKey>()
            {
                @Override
                public void accept(CaseInsensitiveMapKey lookupKey)
                {
                    action.accept(lookupKey.key);
                }
            });
        }
    }

    private class EntrySet implements Set<Map.Entry<String,T>> {

        private Set<Entry<CaseInsensitiveMapKey,T>> wrapped;

        public EntrySet(Set<Entry<CaseInsensitiveMapKey,T>> wrapped)
        {
            this.wrapped = wrapped;
        }


        private List<Map.Entry<String,T>> keyList() {
            return stream().collect(Collectors.toList());
        }

        private Collection<Entry<CaseInsensitiveMapKey,T>> mapCollection(Collection<?> c) {
            return c.stream().map(it -> new CaseInsensitiveEntryAdapter((Entry<String,T>)it)).collect(Collectors.toList());
        }

        @Override
        public int size()
        {
            return wrapped.size();
        }

        @Override
        public boolean isEmpty()
        {
            return wrapped.isEmpty();
        }

        @Override
        public boolean contains(Object o)
        {
            return wrapped.contains(lookupKey(o));
        }

        @Override
        public Iterator<Map.Entry<String,T>> iterator()
        {
            return keyList().iterator();
        }

        @Override
        public Object[] toArray()
        {
            return keyList().toArray();
        }

        @Override
        public <T> T[] toArray(T[] a)
        {
            return keyList().toArray(a);
        }

        @Override
        public boolean add(Entry<String,T> s)
        {
            return wrapped.add(null );
        }

        @Override
        public boolean remove(Object o)
        {
            return wrapped.remove(lookupKey(o));
        }

        @Override
        public boolean containsAll(Collection<?> c)
        {
            return keyList().containsAll(c);
        }

        @Override
        public boolean addAll(Collection<? extends Entry<String,T>> c)
        {
            return wrapped.addAll(mapCollection(c));
        }

        @Override
        public boolean retainAll(Collection<?> c)
        {
            return wrapped.retainAll(mapCollection(c));
        }

        @Override
        public boolean removeAll(Collection<?> c)
        {
            return wrapped.removeAll(mapCollection(c));
        }

        @Override
        public void clear()
        {
            wrapped.clear();
        }

        @Override
        public boolean equals(Object o)
        {
            return wrapped.equals(lookupKey(o));
        }

        @Override
        public int hashCode()
        {
            return wrapped.hashCode();
        }

        @Override
        public Spliterator<Entry<String,T>> spliterator()
        {
            return keyList().spliterator();
        }

        @Override
        public boolean removeIf(Predicate<? super Entry<String, T>> filter)
        {
            return wrapped.removeIf(new Predicate<Entry<CaseInsensitiveMapKey, T>>()
            {
                @Override
                public boolean test(Entry<CaseInsensitiveMapKey, T> entry)
                {
                    return filter.test(new FromCaseInsensitiveEntryAdapter(entry));
                }
            });
        }

        @Override
        public Stream<Entry<String,T>> stream()
        {
            return wrapped.stream().map(it -> new Entry<String, T>()
            {
                @Override
                public String getKey()
                {
                    return it.getKey().key;
                }

                @Override
                public T getValue()
                {
                    return it.getValue();
                }

                @Override
                public T setValue(T value)
                {
                    return it.setValue(value);
                }
            });
        }

        @Override
        public Stream<Map.Entry<String,T>> parallelStream()
        {
            return StreamSupport.stream(spliterator(), true);
        }

        @Override
        public void forEach(Consumer<? super Entry<String, T>> action)
        {
            wrapped.forEach(new Consumer<Entry<CaseInsensitiveMapKey, T>>()
            {
                @Override
                public void accept(Entry<CaseInsensitiveMapKey, T> entry)
                {
                    action.accept(new FromCaseInsensitiveEntryAdapter(entry));
                }
            });
        }
    }

    private class EntryAdapter implements Map.Entry<String,T> {
        private Entry<String,T> wrapped;

        public EntryAdapter(Entry<String, T> wrapped)
        {
            this.wrapped = wrapped;
        }

        @Override
        public String getKey()
        {
            return wrapped.getKey();
        }

        @Override
        public T getValue()
        {
            return wrapped.getValue();
        }

        @Override
        public T setValue(T value)
        {
            return wrapped.setValue(value);
        }

        @Override
        public boolean equals(Object o)
        {
            return wrapped.equals(o);
        }

        @Override
        public int hashCode()
        {
            return wrapped.hashCode();
        }


    }

    private class CaseInsensitiveEntryAdapter implements Map.Entry<CaseInsensitiveMapKey,T> {

        private Entry<String,T> wrapped;

        public CaseInsensitiveEntryAdapter(Entry<String, T> wrapped)
        {
            this.wrapped = wrapped;
        }

        @Override
        public CaseInsensitiveMapKey getKey()
        {
            return lookupKey(wrapped.getKey());
        }

        @Override
        public T getValue()
        {
            return wrapped.getValue();
        }

        @Override
        public T setValue(T value)
        {
            return wrapped.setValue(value);
        }
    }

    private class FromCaseInsensitiveEntryAdapter implements Map.Entry<String,T> {

        private Entry<CaseInsensitiveMapKey,T> wrapped;

        public FromCaseInsensitiveEntryAdapter(Entry<CaseInsensitiveMapKey, T> wrapped)
        {
            this.wrapped = wrapped;
        }

        @Override
        public String getKey()
        {
            return wrapped.getKey().key;
        }

        @Override
        public T getValue()
        {
            return wrapped.getValue();
        }

        @Override
        public T setValue(T value)
        {
            return wrapped.setValue(value);
        }
    }


}
Cagatay Kalan
fuente
0

Debido a esto, estoy creando un nuevo objeto de CaseInsensitiveString para cada evento. Por lo tanto, podría afectar el rendimiento.

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.

static int ciHashCode(String string)
{
    // length and the low 5 bits of hashCode() are case insensitive
    return (string.hashCode() & 0x1f)*33 + string.length();
}
Zdeněk Pavlas
fuente
-3

¿Qué tal el uso de Java 8 streams?

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())
Amarendra Reddy
fuente
Esto no le permite buscar valores en el mapa sin distinción entre mayúsculas y minúsculas.
Gili
equalsignorecase haría eso, ¿no?
Amarendra Reddy
Estás construyendo una lista. OP pidió un mapa.
Gili
Esto destruye el beneficio de complejidad O (1) de un mapa.
Paul Rooney