164 votes

Chaîne insensible à la casse comme clé de HashMap

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

  • Pendant l'initialisation, mon programme crée un 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 capable de localiser l'élément <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 peut affecter les performances.

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

300voto

Roel Spilker Points 2807
Map<String, String> nodeMap = 
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

C'est vraiment tout ce dont vous avez besoin.

57voto

Vishal Points 4548

Comme le suggère Guido García dans leur 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

15voto

Stephen C Points 255558

Une approche consiste à créer une sous-classe personnalisée de l'interface Apache Commons AbstractHashedMap en surchargeant la classe hash y isEqualKeys pour effectuer un hachage et une comparaison des clés sans tenir compte de la casse. (Note - Je n'ai jamais essayé moi-même ...)

Cela permet d'éviter de créer de nouveaux objets chaque fois que vous devez effectuer une consultation ou une mise à jour de la carte. Et le commun Map devrait être de O(1) ... tout comme un ordinateur ordinaire. HashMap .

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


Mais si O(logN) get y put sont acceptables, une TreeMap avec un comparateur de chaînes de caractères insensible à la casse est une option ; par exemple, l'utilisation de String.CASE_INSENSITIVE_ORDER .

Et si cela ne vous dérange pas de créer un nouvel objet temporaire String chaque fois que vous faites un put o get alors la réponse de Vishal est très bien. (Bien que, je note que vous ne préserveriez pas le cas original des clés si vous faisiez cela ...)

6voto

Dave Newton Points 93112

Sous-classe HashMap et créer une version qui met en minuscule la clé sur put y get (et probablement les autres méthodes orientées clé).

Ou composite un HashMap dans la nouvelle classe et tout déléguer à la carte, mais traduire les clés.

Si vous devez conserver la clé d'origine, vous pouvez soit gérer des cartes doubles, soit stocker la clé d'origine avec la valeur.

3voto

Ne serait-il pas préférable d'"envelopper" la chaîne afin de mémoriser le hashCode. Dans la classe normale String, hashCode() est O(N) la première fois et ensuite O(1) puisqu'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().

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X