Zeichenfolge ohne Berücksichtigung der Groß- und Kleinschreibung als HashMap-Schlüssel

178

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?

rs
quelle
3
[Gibt es eine gute Möglichkeit, eine Map <String,?> Groß- und Kleinschreibung ignorieren zu lassen?] [1] [1]: stackoverflow.com/questions/212562/…
Beau Grantham
Ich habe die folgenden Probleme kommentiert, aber sie liegen unter der Schwelle, sodass die Leute sie möglicherweise nicht sehen. Achten Sie darauf, HashMap nicht zu unterordnen. JDK8 hat die Implementierung geändert und Sie müssen jetzt putAll (zumindest) überschreiben, damit diese Vorschläge funktionieren.
Steve N
Dies sollte gut funktionieren. Sie können ein Fliegengewicht verwenden, um die Instanziierung neuer Objekte zu entfernen.
Topkara

Antworten:

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

Das ist wirklich alles was du brauchst.

Roel Spilker
quelle
6
Dies ist bei weitem die einfachste und bewahrt auch den Fall der Schlüssel, wenn sie durchlaufen werden.
Ralf
Das ist schön! Dies war das letzte Puzzleteil zum Erstellen einer geordneten Struktur in ColdFusion, bei der die Verwendung der Punktnotation erhalten bleibt. Anstelle von var struct = {} oder var struct = structnew () können Sie var struct = createObject ('java', 'java.util.TreeMap') verwenden. Init (createObject ('java', 'java.lang.String') ) .CASE_INSENSITIVE_ORDER); FUGLY, aber es funktioniert;)
Eric Fuller
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
Bitte
5
Keine Notwendigkeit für <K extends String>da Stringist endgültig: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker
19
Beachten Sie, dass TreeMap keine konstante Zeit für grundlegende Vorgänge ist. Für die meisten Anwendungen kein Problem, aber es lohnt sich, daran zu denken. Von JavaDoc: "Diese Implementierung bietet garantierte Protokoll (n) -Zeitkosten für die Operationen includeKey, get, put und remove. Algorithmen sind Anpassungen derjenigen in Cormen, Leiserson und Rivests Einführung in Algorithmen."
James Schek
57

Wie von Guido García in ihrer Antwort hier vorgeschlagen :

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

Oder

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

Vishal
quelle
28
Wie wäre es mit enthält, putAll usw.?
Assylias
14
Dies funktioniert in einigen Sprachen wie Türkisch nicht. Google "Der Truthahntest"
Hugo
5
@assylias: true containsKey()und remove()sollte auf die gleiche Weise wie überschrieben werden get(). Die HashMap.putAll()Implementierung verwendet put(), so dass dies kein Problem sein sollte - solange die HashMap-Implementierung gleich bleibt. ;) auch die get()Methodensignatur nimmt ein Objectals Argument, nicht ein String. Der Code super.get(key == null ? null : key.toString().toLowercase());
testet
Beachten Sie, dass Sie, wenn Sie den Kopierkonstruktor benötigen 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.
Sfera
Was ist, wenn die Werte der Karte keine Zeichenfolgen sein sollen? (dh CaseInsensitiveMap<String, Integer>)
Adam Parkin
16

Ein Ansatz besteht darin, eine benutzerdefinierte Unterklasse der Apache Commons- AbstractHashedMapKlasse zu erstellen , wobei die Methoden hashund überschrieben isEqualKeyswerden, 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 MapOperationen sollten O (1) ... genau wie eine reguläre HashMap.

Und wenn Sie bereit sind, die von ihnen getroffenen Implementierungsentscheidungen zu akzeptieren, CaseInsensitiveMapübernimmt Apache Commons das Anpassen / Spezialisieren AbstractHashedMapfür Sie.


Wenn jedoch O (logN) getund putOperationen akzeptabel sind, TreeMapist 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 putoder getausfü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 ...)

Stephen C.
quelle
6

Unterklasse HashMapund erstellen Sie eine Version, die den Schlüssel auf putund 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.

Dave Newton
quelle
Meinen Sie eine String.toLowerCase () während der Suche?
rs
@ user710178 Nicht nur während der Suche, sondern auch während der Speicherung.
Dave Newton
@ user710178 Oh, richtig, wie diese andere Antwort zeigt, existiert dies bereits, wenn Sie nichts gegen eine zusätzliche Abhängigkeit haben.
Dave Newton
@StephenC Wenn es Ihren Anforderungen entspricht, sicher; OP hat a angegeben 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?)
Dave Newton
1
Für JDK 8 und höher müssen Sie putAll (zumindest) überschreiben, da sich die Implementierung geändert hat.
Steve N
4

Mir fallen zwei Möglichkeiten ein:

  1. Sie können das direkt s.toUpperCase().hashCode();als Schlüssel für das verwendenMap .
  2. Sie können a TreeMap<String>mit einem Benutzerdefinierten verwenden Comparator, 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.

Gabriel Belingueres
quelle
3

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.

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

Dies würde es Ihnen ermöglichen, jede Implementierung von Hashtable in Java zu verwenden und O (1) hasCode () zu haben.

le-doude
quelle
3

Sie können eine Verwendung HashingStrategy basierend Mapvon Eclipse - Sammlungen

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

Hinweis: Ich bin ein Mitarbeiter von Eclipse Collections.

Nikhil Nanivadekar
quelle
2

Basierend auf anderen Antworten gibt es grundsätzlich zwei Ansätze: Unterklassen HashMapoder Wrapping String. 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 LocaleIn- StringCase-Operation angeben . Sie würden also neue Methoden erstellen ( get(String, Locale), ...). Alles ist einfacher und klarer.

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
}

Und nun zu Ihren Leistungssorgen: Vorzeitige Optimierung ist die Wurzel allen Übels :)

Sinuhepop
quelle
2
"Und nun, über Ihre Leistungssorgen: Vorzeitige Optimierung ist die Wurzel allen Übels :)" - Im Gegenteil, es ist böse, dies als Ausrede zu verwenden, um immer ineffizienten Code zu schreiben.
Gordon
1
Eigentlich @Gordon, beide sind je nach Kontext gleich schlecht. Das Label "böse" ist ein Zeichen für Schwarz-Weiß-Denken, wie "Best Practice" und verschiedene andere nicht hilfreiche Ausdrücke, die viele IT-Mitarbeiter verwenden. Am besten ganz vermeiden.
Stephen C
Ich habe festgestellt, dass es weniger dazu führt, dass man den Leuten sagt, dass sie nicht "Best Practice" befolgen, als dass man ihnen sagt, dass sie schlechte Praktiken haben.
Gordon
0

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;

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
quelle
0

Aus diesem Grund erstelle ich für jedes Ereignis ein neues Objekt von CaseInsensitiveString. Es könnte also die Leistung beeinträchtigen.

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.

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
quelle
-3

Wie wäre es mit Java 8 Streams.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())
Amarendra Reddy
quelle
Auf diese Weise können Sie die Werte in der Karte nicht ohne Berücksichtigung der Groß- und Kleinschreibung nachschlagen.
Gili
equalsignorecase würde das tun, nicht wahr?
Amarendra Reddy
Sie erstellen eine Liste. OP bat um eine Karte.
Gili
Dies zerstört den O (1) -Komplexitätsvorteil einer Karte.
Paul Rooney