Stringa senza distinzione tra maiuscole e minuscole come chiave HashMap


178

Vorrei utilizzare la stringa senza distinzione tra maiuscole e minuscole come chiave HashMap per i seguenti motivi.

  • Durante l'inizializzazione, il mio programma crea HashMap con String definito dall'utente
  • Durante l'elaborazione di un evento (traffico di rete nel mio caso), potrei ricevere String in un caso diverso, ma dovrei essere in grado di localizzare <key, value>da HashMap ignorando il caso che ho ricevuto dal traffico.

Ho seguito questo approccio

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()));

Per questo motivo, sto creando un nuovo oggetto di CaseInsensitiveString per ogni evento. Quindi, potrebbe influire sulle prestazioni.

Esiste un altro modo per risolvere questo problema?


3
[C'è un buon modo per avere una mappa <String,?> Get and put ignore case?] [1] [1]: stackoverflow.com/questions/212562/…
Beau Grantham,

Ho commentato i problemi seguenti, ma sono al di sotto della soglia, quindi le persone potrebbero non vederli. Fai attenzione alla sottoclasse di HashMap. JDK8 ha modificato l'implementazione e ora è necessario sovrascrivere putAll (almeno) per far funzionare quei suggerimenti.
Steve N,

Questo dovrebbe funzionare bene. È possibile utilizzare un peso mosca per sbarazzarsi della nuova istanza di oggetto.
topkara,

Risposte:


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

Questo è davvero tutto ciò di cui hai bisogno.


6
Questo è di gran lunga il più semplice e conserva anche il caso dei tasti quando li scorre.
Ralf,

Questo è bellissimo! Questo è stato l'ultimo pezzo del puzzle per creare una struttura ordinata in ColdFusion che preserva la capacità di usare la notazione a punti. Invece di var struct = {} o var struct = structnew () puoi usare var struct = createObject ('java', 'java.util.TreeMap'). Init (createObject ('java', 'java.lang.String' ) .CASE_INSENSITIVE_ORDER); FUGAMENTE, ma funziona;)
Eric Fuller,

public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
Arrivo il

5
Non è necessario <K extends String>poiché Stringè definitivo: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker,

19
Tieni presente che TreeMap non è un tempo costante per le operazioni di base. Non è un problema per la maggior parte delle applicazioni, ma vale la pena ricordare. Da JavaDoc: "Questa implementazione fornisce costi di log (n) di tempo garantiti per le operazioni contenuteKey, get, put e remove. Gli algoritmi sono adattamenti di quelli di Cormen, Leiserson e Rivest's Introduction to Algorithms."
James Schek,

57

Come suggerito da Guido García nella loro risposta qui :

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


28
Che ne dici di contiene, putAll, ecc.?
Assylias,

14
Questo non funziona in alcune lingue, come il turco. Google "The turkey test"
Hugo

5
@assylias: vero, containsKey()e remove()dovrebbe essere sostituito allo stesso modo di get(). l' HashMap.putAll()implementazione utilizza put(), quindi non dovrebbe essere un problema, purché l'implementazione di HashMap rimanga invariata. ;) anche la get()firma del metodo prende Objectcome argomento, non a String. il codice inoltre non verifica una chiave nulla:super.get(key == null ? null : key.toString().toLowercase());
sfera

si noti che se si richiede il costruttore di copia HashMap(<? extends String, ? extends String> anotherMap), non si deve chiamare la super implementazione dello stesso costruttore in quanto tale operazione non garantirà che le chiavi siano in minuscolo. potresti usare: super(anotherMap.size()); putAll(anotherMap);invece.
sfera

Cosa succede se si desidera che i valori della mappa non siano stringhe? (ie CaseInsensitiveMap<String, Integer>)
Adam Parkin,

16

Un approccio consiste nel creare una sottoclasse personalizzata della AbstractHashedMapclasse Apache Commons , sovrascrivendo i metodi hashe isEqualKeysper eseguire l'hashing senza distinzione tra maiuscole e minuscole e il confronto delle chiavi. (Nota: non l'ho mai provato da solo ...)

Ciò evita il sovraccarico di creare nuovi oggetti ogni volta che è necessario eseguire una ricerca o un aggiornamento della mappa. E le Mapoperazioni comuni dovrebbero O (1) ... proprio come un normale HashMap.

E se sei pronto ad accettare le scelte di implementazione che hanno fatto, Apache Commons CaseInsensitiveMapfa il lavoro di personalizzazione / specializzazione AbstractHashedMapper te.


Ma se O (logN) gete le putoperazioni sono accettabili, TreeMapun'opzione con un comparatore di stringhe senza distinzione tra maiuscole e minuscole è un'opzione; ad es String.CASE_INSENSITIVE_ORDER. usando .

E se non ti dispiace creare un nuovo oggetto String temporaneo ogni volta che fai un puto get, allora la risposta di Vishal va bene. (Tuttavia, noto che non conservereste la custodia originale delle chiavi se lo faceste ...)


6

Sottoclasse HashMape creare una versione in minuscolo sulla chiave pute get(e probabilmente sugli altri metodi orientati alla chiave).

Oppure componi a HashMapnella nuova classe e delega tutto alla mappa, ma traduci le chiavi.

Se è necessario conservare la chiave originale, è possibile mantenere mappe doppie o memorizzare la chiave originale insieme al valore.


Vuoi dire fare un String.toLowerCase () durante la ricerca?
RS

@ user710178 Non solo durante la ricerca, ma anche durante la memorizzazione.
Dave Newton,

@ user710178 Oh, giusto, come sottolinea l'altra risposta, questo esiste già, se non ti dispiace una dipendenza aggiuntiva.
Dave Newton,

@StephenC Se soddisfa le tue esigenze, certo; OP ha specificato a HashMap, quindi è quello con cui sono andato :) Oh, intendi quello Commons; Vedo. Immagino, fintanto che non ne hai bisogno (o hanno finalmente i generici adesso?)
Dave Newton,

1
Per JDK 8 e versioni successive dovrai anche (almeno) sostituire putAll poiché l'implementazione è cambiata.
Steve N,

4

Mi vengono in mente due scelte:

  1. È possibile utilizzare direttamente il s.toUpperCase().hashCode();come chiave delMap .
  2. È possibile utilizzare a TreeMap<String>con un'abitudine Comparatorche ignora il caso.

Altrimenti, se preferisci la tua soluzione, invece di definire un nuovo tipo di stringa, preferirei implementare una nuova mappa con la funzionalità di insensibilità al caso richiesta.


3

Non sarebbe meglio "avvolgere" la stringa per memorizzare l'hashCode. Nella normale classe String hashCode () è O (N) la prima volta e poi è O (1) poiché viene conservato per un 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.
}

Ciò consentirebbe di utilizzare qualsiasi implementazione di Hashtable in Java e di avere O (1) hasCode ().


3

È possibile utilizzare un HashingStrategy basato Mapsu Eclipse Collections

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

Nota: sono un collaboratore delle raccolte Eclipse.


2

Sulla base di altre risposte, ci sono fondamentalmente due approcci: sottoclasse HashMapo wrapping String. Il primo richiede un po 'più di lavoro. In effetti, se vuoi farlo correttamente, devi sovrascrivere quasi tutti i metodi ( containsKey, entrySet, get, put, putAll and remove).

Comunque, ha un problema. Se si desidera evitare problemi futuri, è necessario specificare un caso Localenel Stringcaso. Quindi creeresti nuovi metodi ( get(String, Locale), ...). String è tutto più facile e più chiaro:

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
}

E bene, riguardo alle tue preoccupazioni sulle prestazioni: l'ottimizzazione prematura è la radice di tutti i mali :)


2
"E bene, riguardo alle tue preoccupazioni sulle prestazioni: l'ottimizzazione prematura è la radice di tutti i mali :)" - Al contrario, usarlo come scusa per scrivere sempre codice inefficiente è ciò che è male.
Gordon,

1
In realtà @Gordon, entrambi sono ugualmente cattivi, a seconda del contesto. L'etichetta "male" è un segno del pensiero in bianco e nero, come "best practice" e varie altre frasi inutili che molte persone IT tendono ad usare. Meglio evitarlo del tutto.
Stephen C

Ho scoperto che dire alle persone che non stanno seguendo le "migliori pratiche" tende a produrre meno scosse che non dire loro che hanno cattive pratiche.
Gordon,

0

Questo è un adattatore per HashMaps che ho implementato per un progetto recente. Funziona in modo simile a quello che fa @SandyR, ma incapsula la logica di conversione in modo da non convertire manualmente le stringhe in un oggetto wrapper.

Ho usato le funzionalità di Java 8 ma con alcune modifiche, è possibile adattarlo alle versioni precedenti. L'ho testato per gli scenari più comuni, ad eccezione delle nuove funzioni di streaming Java 8.

Fondamentalmente avvolge una HashMap, indirizza tutte le funzioni su di essa durante la conversione di stringhe in / da un oggetto wrapper. Ma ho dovuto anche adattare KeySet e EntrySet perché inoltrano alcune funzioni alla mappa stessa. Quindi restituisco due nuovi set per chiavi e voci che avvolgono effettivamente keySet () e entrySet () originali.

Una nota: Java 8 ha modificato l'implementazione del metodo putAll che non sono riuscito a trovare un modo semplice per sovrascrivere. Pertanto, l'implementazione corrente potrebbe avere prestazioni ridotte, soprattutto se si utilizza putAll () per un set di dati di grandi dimensioni.

Per favore fatemi sapere se trovate un bug o avete suggerimenti per migliorare il codice.

pacchetto 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);
        }
    }


}

0

Per questo motivo, sto creando un nuovo oggetto di CaseInsensitiveString per ogni evento. Quindi, potrebbe influire sulle prestazioni.

La creazione di wrapper o la conversione della chiave in minuscolo prima della ricerca creano entrambi nuovi oggetti. Scrivere la tua implementazione java.util.Map è l'unico modo per evitarlo. Non è troppo difficile e ne vale la pena. Ho trovato la seguente funzione hash per funzionare abbastanza bene, fino a poche centinaia di tasti.

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

-3

Che ne dici di usare java 8 stream.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())

Ciò non consente di cercare valori nella mappa in modo insensibile alle maiuscole / minuscole.
Gili,

equalsignorecase farebbe no?
Amarendra Reddy,

Stai creando un elenco. OP ha chiesto una mappa.
Gili

Questo distrugge il vantaggio della complessità O (1) di una mappa.
Paul Rooney,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.