Chaîne insensible à la casse en tant que clé HashMap

178

Je voudrais utiliser une chaîne insensible à la casse comme clé HashMap pour les raisons suivantes.

  • Lors de l'initialisation, mon programme crée HashMap avec une chaîne définie par l'utilisateur
  • Lors du traitement d'un événement (trafic réseau dans mon cas), il se peut que je reçoive String dans un cas différent, mais je devrais être en mesure de localiser le <key, value>de HashMap en ignorant le cas que j'ai reçu du trafic.

J'ai suivi cette approche

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

Pour cette raison, je crée un nouvel objet de CaseInsensitiveString pour chaque événement. Donc, cela pourrait nuire aux performances.

Existe-t-il un autre moyen de résoudre ce problème?

rs
la source
3
[Y a-t-il un bon moyen d'avoir une Map <String,?> Obtenir et mettre la casse ignorée?] [1] [1]: stackoverflow.com/questions/212562/…
Beau Grantham
J'ai commenté les problèmes ci-dessous, mais ils sont en dessous du seuil, de sorte que les gens peuvent ne pas les voir. Méfiez-vous des sous-classements de HashMap. JDK8 a changé l'implémentation et vous devez maintenant remplacer putAll (au moins) pour que ces suggestions fonctionnent.
Steve N
Cela devrait fonctionner correctement. Vous pouvez utiliser un poids mouche pour vous débarrasser de la nouvelle instanciation d'objet.
topkara

Réponses:

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

C'est vraiment tout ce dont vous avez besoin.

Roel Spilker
la source
6
C'est de loin le plus simple et préserve également la casse des clés lors de leur itération.
Ralf
C'est beau! C'était la dernière pièce du puzzle pour créer une structure ordonnée dans ColdFusion qui préserve la possibilité d'utiliser la notation par points. Au lieu de var struct = {} ou var struct = structnew (), vous pouvez utiliser var struct = createObject ('java', 'java.util.TreeMap'). Init (createObject ('java', 'java.lang.String' ) .CASE_INSENSITIVE_ORDER); FUGLY, mais ça marche;)
Eric Fuller
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
pllee
5
Pas besoin de <K extends String>puisque Stringc'est définitif: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker
19
Gardez à l'esprit que TreeMap n'est pas un temps constant pour les opérations de base. Ce n'est pas un problème pour la plupart des applications, mais il vaut la peine de le garder à l'esprit. De JavaDoc: "Cette implémentation fournit un coût en temps log (n) garanti pour les opérations containsKey, get, put et remove. Les algorithmes sont des adaptations de ceux de Cormen, Leiserson et Introduction aux algorithmes de Rivest."
James Schek
57

Comme suggéré par Guido García dans sa réponse ici :

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

Ou

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

Vishal
la source
28
Que diriez-vous de contains, putAll, etc.?
assylias
14
Cela ne fonctionne pas dans certaines langues, comme le turc. Google "Le test de la dinde"
Hugo
5
@assylias: true containsKey()et remove()doit être remplacé de la même manière que get(). l' HashMap.putAll()implémentation utilise put(), donc cela ne devrait pas être un problème - tant que l'implémentation HashMap reste la même. ;) aussi la get()signature de méthode prend un Objectcomme argument, pas un String. le code ne teste pas non plus une clé nulle:super.get(key == null ? null : key.toString().toLowercase());
sfera
notez que si vous avez besoin du constructeur de copie HashMap(<? extends String, ? extends String> anotherMap), vous ne devriez pas appeler la super implémentation du même constructeur car cette opération ne garantira pas que vos clés sont en minuscules. vous pouvez utiliser: à la super(anotherMap.size()); putAll(anotherMap);place.
sfera
Que faire si vous souhaitez que les valeurs de la carte ne soient pas des chaînes? (ie CaseInsensitiveMap<String, Integer>)
Adam Parkin
16

Une approche consiste à créer une sous-classe personnalisée de la classe Apache Commons AbstractHashedMap, en remplaçant les méthodes hashet isEqualKeyspour effectuer un hachage insensible à la casse et une comparaison des clés. (Remarque - je n'ai jamais essayé cela moi-même ...)

Cela évite d'avoir à créer de nouveaux objets à chaque fois que vous devez effectuer une recherche ou une mise à jour de carte. Et les Mapopérations courantes devraient O (1) ... tout comme un régulier HashMap.

Et si vous êtes prêt à accepter les choix d'implémentation qu'ils ont faits, Apache Commons CaseInsensitiveMapfait le travail de personnalisation / spécialisation AbstractHashedMappour vous.


Mais si O (logN) getet les putopérations sont acceptables, un TreeMapavec un comparateur de chaîne insensible à la casse est une option; par exemple en utilisant String.CASE_INSENSITIVE_ORDER.

Et si cela ne vous dérange pas de créer un nouvel objet String temporaire à chaque fois que vous faites un putou get, la réponse de Vishal est très bien. (Cependant, je note que vous ne conserveriez pas l'étui d'origine des clés si vous faisiez cela ...)

Stephen C
la source
6

Sous HashMap- classez et créez une version qui minuscule la clé sur putet get(et probablement les autres méthodes orientées clé).

Ou composez a HashMapdans la nouvelle classe et déléguez tout à la carte, mais traduisez les clés.

Si vous devez conserver la clé d'origine, vous pouvez soit conserver deux cartes, soit stocker la clé d'origine avec la valeur.

Dave Newton
la source
Voulez-vous dire faire un String.toLowerCase () pendant la recherche?
rs
@ user710178 Pas seulement pendant la recherche, mais aussi pendant le stockage.
Dave Newton
@ user710178 Oh, d'accord, comme cette autre réponse le souligne, cela existe déjà, si cela ne vous dérange pas une dépendance supplémentaire.
Dave Newton
@StephenC Si cela répond à vos besoins, bien sûr; OP a spécifié un HashMap, donc c'est ce que j'ai choisi :) Oh, vous voulez dire celui des Communes; Je vois. J'imagine, tant que vous n'en avez pas besoin (ou ont-ils enfin des génériques maintenant?)
Dave Newton
1
Pour JDK 8 et supérieur, vous devrez également (au moins) remplacer putAll car l'implémentation a changé.
Steve N
4

Deux choix me viennent à l'esprit:

  1. Vous pouvez utiliser directement le s.toUpperCase().hashCode();comme clé du Map.
  2. Vous pouvez utiliser un TreeMap<String>avec une coutume Comparatorqui ignore la casse.

Sinon, si vous préférez votre solution, au lieu de définir un nouveau type de chaîne, je préférerais implémenter une nouvelle carte avec la fonctionnalité d'insensibilité à la casse requise.

Gabriel Belingueres
la source
3

Ne serait-il pas préférable de "envelopper" la chaîne afin de mémoriser le hashCode. Dans la classe String normale, hashCode () est O (N) la première fois, puis O (1) car il est conservé pour une utilisation future.

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

Cela vous permettrait d'utiliser n'importe quelle implémentation de Hashtable en java et d'avoir O (1) hasCode ().

le-doude
la source
3

Vous pouvez utiliser une stratégie de hachage basée Mapsur des collections Eclipse

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

Remarque: je suis un contributeur aux collections Eclipse.

Nikhil Nanivadekar
la source
2

Sur la base d'autres réponses, il existe essentiellement deux approches: le sous HashMap-classement ou l'encapsulation String. Le premier demande un peu plus de travail. En fait, si vous voulez le faire correctement, vous devez remplacer presque toutes les méthodes ( containsKey, entrySet, get, put, putAll and remove).

Quoi qu'il en soit, il a un problème. Si vous souhaitez éviter de futurs problèmes, vous devez spécifier une opération Localein Stringcase. Vous créeriez donc de nouvelles méthodes ( get(String, Locale), ...). Tout est plus facile et plus clair pour envelopper String:

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
}

Et bien, à propos de vos inquiétudes sur les performances: l' optimisation prématurée est la racine de tout mal :)

sinuhepop
la source
2
"Et bien, à propos de vos inquiétudes sur les performances: l'optimisation prématurée est la racine de tout mal :)" - Au contraire, utiliser cela comme excuse pour toujours écrire du code inefficace est ce qui est mal.
Gordon
1
En fait @Gordon, les deux sont tout aussi mauvais, selon le contexte. L'étiquette «mal» est un signe de pensée en noir et blanc, comme «meilleure pratique» et diverses autres expressions inutiles que de nombreux informaticiens ont tendance à utiliser. Mieux vaut l'éviter complètement.
Stephen C
J'ai constaté que le fait de dire aux gens qu'ils ne suivent pas les «meilleures pratiques» a tendance à produire moins de fouilles que de leur dire qu'ils ont de mauvaises pratiques.
Gordon le
0

Il s'agit d'un adaptateur pour HashMaps que j'ai implémenté pour un projet récent. Fonctionne d'une manière similaire à ce que fait @SandyR, mais encapsule la logique de conversion afin que vous ne convertissiez pas manuellement les chaînes en objet wrapper.

J'ai utilisé les fonctionnalités de Java 8 mais avec quelques modifications, vous pouvez l'adapter aux versions précédentes. Je l'ai testé pour la plupart des scénarios courants, à l'exception des nouvelles fonctions de flux Java 8.

Fondamentalement, il enveloppe un HashMap, y dirige toutes les fonctions lors de la conversion de chaînes vers / depuis un objet wrapper. Mais j'ai dû également adapter KeySet et EntrySet car ils transmettent certaines fonctions à la carte elle-même. Je renvoie donc deux nouveaux ensembles pour les clés et les entrées qui enveloppent réellement le keySet () et entrySet () d'origine.

Une note: Java 8 a changé l'implémentation de la méthode putAll que je n'ai pas trouvé de moyen facile de remplacer. L'implémentation actuelle peut donc avoir des performances dégradées, en particulier si vous utilisez putAll () pour un ensemble de données volumineux.

Veuillez me faire savoir si vous trouvez un bogue ou avez des suggestions pour améliorer le code.

package 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
la source
0

Pour cette raison, je crée un nouvel objet de CaseInsensitiveString pour chaque événement. Donc, cela pourrait nuire aux performances.

La création d'encapsuleurs ou la conversion de la clé en minuscules avant la recherche créent de nouveaux objets. Ecrire votre propre implémentation java.util.Map est le seul moyen d'éviter cela. Ce n'est pas trop difficile et l'OMI en vaut la peine. J'ai trouvé que la fonction de hachage suivante fonctionnait plutôt bien, jusqu'à quelques centaines de clés.

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
la source
-3

Que diriez-vous d'utiliser les flux java 8.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())
Amarendra Reddy
la source
Cela ne vous permet pas de rechercher des valeurs dans la carte sans tenir compte de la casse.
Gili
equalsignorecase ferait cela n'est-ce pas?
Amarendra Reddy
Vous construisez une liste. OP a demandé une carte.
Gili
Cela détruit l'avantage de complexité O (1) d'une carte.
Paul Rooney