Ich möchte aus den folgenden Gründen eine Zeichenfolge ohne Berücksichtigung der Groß- und Kleinschreibung als HashMap-Schlüssel verwenden.
- Während der Initialisierung erstellt mein Programm HashMap mit einer benutzerdefinierten Zeichenfolge
- Während der Verarbeitung eines Ereignisses (in meinem Fall Netzwerkverkehr) habe ich möglicherweise in einem anderen Fall einen String erhalten, aber ich sollte in der Lage sein, den
<key, value>
von HashMap zu finden, ohne den Fall zu beachten , den ich vom Verkehr erhalten habe.
Ich habe diesen Ansatz verfolgt
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()));
Aus diesem Grund erstelle ich für jedes Ereignis ein neues Objekt von CaseInsensitiveString. Es könnte also die Leistung beeinträchtigen.
Gibt es eine andere Möglichkeit, dieses Problem zu lösen?
Antworten:
Das ist wirklich alles was du brauchst.
quelle
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
<K extends String>
daString
ist endgültig:public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Wie von Guido García in ihrer Antwort hier vorgeschlagen :
Oder
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
quelle
containsKey()
undremove()
sollte auf die gleiche Weise wie überschrieben werdenget()
. DieHashMap.putAll()
Implementierung verwendetput()
, so dass dies kein Problem sein sollte - solange die HashMap-Implementierung gleich bleibt. ;) auch dieget()
Methodensignatur nimmt einObject
als Argument, nicht einString
. Der Codesuper.get(key == null ? null : key.toString().toLowercase());
HashMap(<? extends String, ? extends String> anotherMap)
, nicht die Superimplementierung desselben Konstruktors aufrufen sollten, da diese Operation nicht garantiert, dass Ihre Schlüssel in Kleinbuchstaben geschrieben sind. Sie könnten verwenden:super(anotherMap.size()); putAll(anotherMap);
stattdessen.CaseInsensitiveMap<String, Integer>
)Ein Ansatz besteht darin, eine benutzerdefinierte Unterklasse der Apache Commons-
AbstractHashedMap
Klasse zu erstellen , wobei die Methodenhash
und überschriebenisEqualKeys
werden, um das Hashing ohne Berücksichtigung der Groß- und Kleinschreibung und den Vergleich von Schlüsseln durchzuführen. (Hinweis - ich habe das selbst noch nie versucht ...)Dies vermeidet den Aufwand, jedes Mal neue Objekte zu erstellen, wenn Sie eine Kartensuche oder -aktualisierung durchführen müssen. Und die üblichen
Map
Operationen sollten O (1) ... genau wie eine reguläreHashMap
.Und wenn Sie bereit sind, die von ihnen getroffenen Implementierungsentscheidungen zu akzeptieren,
CaseInsensitiveMap
übernimmt Apache Commons das Anpassen / SpezialisierenAbstractHashedMap
für Sie.Wenn jedoch O (logN)
get
undput
Operationen akzeptabel sind,TreeMap
ist ein Komparator für Zeichenfolgen ohne Berücksichtigung der Groß- und Kleinschreibung eine Option. zB mitString.CASE_INSENSITIVE_ORDER
.Und wenn es Ihnen nichts ausmacht, jedes Mal, wenn Sie ein
put
oderget
ausführen , ein neues temporäres String-Objekt zu erstellen , ist Vishals Antwort in Ordnung. (Ich stelle jedoch fest, dass Sie den ursprünglichen Fall der Schlüssel nicht beibehalten würden, wenn Sie dies tun würden ...)quelle
Unterklasse
HashMap
und erstellen Sie eine Version, die den Schlüssel aufput
und in Kleinbuchstaben schreibtget
(und wahrscheinlich die anderen schlüsselorientierten Methoden) in .Oder zusammengesetzt a
HashMap
in die neue Klasse ein und delegieren Sie alles an die Karte, aber übersetzen Sie die Schlüssel.Wenn Sie den Originalschlüssel behalten müssen, können Sie entweder zwei Karten verwalten oder den Originalschlüssel zusammen mit dem Wert speichern.
quelle
HashMap
, also habe ich das gemacht :) Oh, du meinst das Commons; Aha. Ich denke, solange Sie es nicht generieren müssen (oder haben sie jetzt endlich Generika?)Mir fallen zwei Möglichkeiten ein:
s.toUpperCase().hashCode();
als Schlüssel für das verwendenMap
.TreeMap<String>
mit einem Benutzerdefinierten verwendenComparator
, der den Fall ignoriert.Andernfalls würde ich, wenn Sie Ihre Lösung bevorzugen, anstatt eine neue Art von Zeichenfolge zu definieren, lieber eine neue Map mit der erforderlichen Groß- und Kleinschreibung implementieren.
quelle
Wäre es nicht besser, den String zu "verpacken", um sich den Hashcode zu merken? In der normalen String-Klasse ist hashCode () zum ersten Mal O (N) und dann O (1), da es für die zukünftige Verwendung aufbewahrt wird.
Dies würde es Ihnen ermöglichen, jede Implementierung von Hashtable in Java zu verwenden und O (1) hasCode () zu haben.
quelle
Sie können eine Verwendung HashingStrategy basierend
Map
von Eclipse - SammlungenHinweis: Ich bin ein Mitarbeiter von Eclipse Collections.
quelle
Basierend auf anderen Antworten gibt es grundsätzlich zwei Ansätze: Unterklassen
HashMap
oder WrappingString
. Der erste erfordert etwas mehr Arbeit. Wenn Sie es richtig machen möchten, müssen Sie fast alle Methoden überschreiben (containsKey, entrySet, get, put, putAll and remove
).Wie auch immer, es hat ein Problem. Wenn Sie zukünftige Probleme vermeiden möchten, müssen Sie eine
Locale
In-String
Case-Operation angeben . Sie würden also neue Methoden erstellen (get(String, Locale)
, ...). Alles ist einfacher und klarer.Und nun zu Ihren Leistungssorgen: Vorzeitige Optimierung ist die Wurzel allen Übels :)
quelle
Dies ist ein Adapter für HashMaps, den ich kürzlich für ein Projekt implementiert habe. Funktioniert ähnlich wie @SandyR, kapselt jedoch die Konvertierungslogik, sodass Sie Zeichenfolgen nicht manuell in ein Wrapper-Objekt konvertieren.
Ich habe Java 8-Funktionen verwendet, aber mit ein paar Änderungen können Sie es an frühere Versionen anpassen. Ich habe es für die meisten gängigen Szenarien getestet, mit Ausnahme der neuen Java 8-Stream-Funktionen.
Grundsätzlich umschließt es eine HashMap, leitet alle Funktionen an sie weiter, während Zeichenfolgen in ein Wrapper-Objekt konvertiert werden. Ich musste aber auch KeySet und EntrySet anpassen, da sie einige Funktionen an die Karte selbst weiterleiten. Also gebe ich zwei neue Sets für Schlüssel und Einträge zurück, die das ursprüngliche keySet () und entrySet () umschließen.
Ein Hinweis: Java 8 hat die Implementierung der putAll-Methode geändert, die ich nicht einfach überschreiben konnte. Daher kann die aktuelle Implementierung die Leistung beeinträchtigen, insbesondere wenn Sie putAll () für einen großen Datensatz verwenden.
Bitte lassen Sie mich wissen, wenn Sie einen Fehler finden oder Vorschläge zur Verbesserung des Codes haben.
Paket webbit.collections;
quelle
Durch das Erstellen von Wrappern oder das Konvertieren von Schlüsseln in Kleinbuchstaben vor der Suche werden neue Objekte erstellt. Das Schreiben einer eigenen java.util.Map-Implementierung ist der einzige Weg, dies zu vermeiden. Es ist nicht zu schwer und IMO ist es wert. Ich fand die folgende Hash-Funktion ziemlich gut, bis zu ein paar hundert Tasten.
quelle
Wie wäre es mit Java 8 Streams.
quelle