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?
Réponses:
C'est vraiment tout ce dont vous avez besoin.
la source
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
<K extends String>
puisqueString
c'est définitif:public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Comme suggéré par Guido García dans sa réponse ici :
Ou
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
la source
containsKey()
etremove()
doit être remplacé de la même manière queget()
. l'HashMap.putAll()
implémentation utiliseput()
, donc cela ne devrait pas être un problème - tant que l'implémentation HashMap reste la même. ;) aussi laget()
signature de méthode prend unObject
comme argument, pas unString
. le code ne teste pas non plus une clé nulle:super.get(key == null ? null : key.toString().toLowercase());
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: à lasuper(anotherMap.size()); putAll(anotherMap);
place.CaseInsensitiveMap<String, Integer>
)Une approche consiste à créer une sous-classe personnalisée de la classe Apache Commons
AbstractHashedMap
, en remplaçant les méthodeshash
etisEqualKeys
pour 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
Map
opérations courantes devraient O (1) ... tout comme un régulierHashMap
.Et si vous êtes prêt à accepter les choix d'implémentation qu'ils ont faits, Apache Commons
CaseInsensitiveMap
fait le travail de personnalisation / spécialisationAbstractHashedMap
pour vous.Mais si O (logN)
get
et lesput
opérations sont acceptables, unTreeMap
avec un comparateur de chaîne insensible à la casse est une option; par exemple en utilisantString.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
put
ouget
, 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 ...)la source
Sous
HashMap
- classez et créez une version qui minuscule la clé surput
etget
(et probablement les autres méthodes orientées clé).Ou composez a
HashMap
dans 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.
la source
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?)Deux choix me viennent à l'esprit:
s.toUpperCase().hashCode();
comme clé duMap
.TreeMap<String>
avec une coutumeComparator
qui 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.
la source
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.
Cela vous permettrait d'utiliser n'importe quelle implémentation de Hashtable en java et d'avoir O (1) hasCode ().
la source
Vous pouvez utiliser une stratégie de hachage basée
Map
sur des collections EclipseRemarque: je suis un contributeur aux collections Eclipse.
la source
Sur la base d'autres réponses, il existe essentiellement deux approches: le sous
HashMap
-classement ou l'encapsulationString
. 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
Locale
inString
case. Vous créeriez donc de nouvelles méthodes (get(String, Locale)
, ...). Tout est plus facile et plus clair pour envelopper String:Et bien, à propos de vos inquiétudes sur les performances: l' optimisation prématurée est la racine de tout mal :)
la source
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;
la source
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.
la source
Que diriez-vous d'utiliser les flux java 8.
la source