Chciałbym użyć łańcucha niewrażliwego na wielkość liter jako klucza HashMap z następujących powodów.
- Podczas inicjalizacji mój program tworzy HashMap ze zdefiniowanym przez użytkownika ciągiem znaków
- Podczas przetwarzania zdarzenia (ruchu sieciowego w moim przypadku), mogłem otrzymać String w innym przypadku, ale powinienem być w stanie zlokalizować
<key, value>
z HashMap ignorując przypadek, który otrzymałem z ruchu.
Postępowałem zgodnie z tym podejściem
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()));
Z tego powodu tworzę nowy obiekt CaseInsensitiveString dla każdego zdarzenia. Może więc uderzyć w wydajność.
Czy istnieje inny sposób rozwiązania tego problemu?
Odpowiedzi:
To naprawdę wszystko, czego potrzebujesz.
źródło
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
<K extends String>
ponieważString
jest ostateczny:public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Jak zasugerował Guido García w odpowiedzi tutaj :
Lub
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
źródło
containsKey()
iremove()
powinno zostać zastąpione w taki sam sposób, jakget()
. zHashMap.putAll()
zastosowania wdrożenioweput()
, więc nie powinno być problemu - tak długo, jak pobytów realizacji HashMap samo. ;) równieżget()
sygnatura metody przyjmujeObject
jako argument, a nieString
. kod również nie sprawdza klucza pustego:super.get(key == null ? null : key.toString().toLowercase());
HashMap(<? extends String, ? extends String> anotherMap)
, nie powinieneś wywoływać super implementacji tego samego konstruktora, ponieważ ta operacja nie zagwarantuje, że twoje klucze będą pisane małymi literami. możesz użyć:super(anotherMap.size()); putAll(anotherMap);
zamiast.CaseInsensitiveMap<String, Integer>
)Jednym z podejść jest utworzenie niestandardowej podklasy
AbstractHashedMap
klasy Apache Commons , nadpisując metodyhash
i wisEqualKeys
celu wykonania mieszania i porównywania kluczy bez uwzględniania wielkości liter. (Uwaga - sam nigdy tego nie próbowałem ...)Pozwala to uniknąć nakładu pracy związanego z tworzeniem nowych obiektów za każdym razem, gdy trzeba przeszukać mapę lub zaktualizować ją. A typowe
Map
operacje powinny O (1) ... tak jak zwykłyHashMap
.A jeśli jesteś przygotowany na zaakceptowanie dokonanych przez nich wyborów dotyczących implementacji, Apache Commons
CaseInsensitiveMap
wykonaAbstractHashedMap
za Ciebie pracę dostosowywania / specjalizacji .Ale jeśli O (logN)
get
iput
operacje są dopuszczalne,TreeMap
opcją jest a z komparatorem bez rozróżniania wielkości liter; npString.CASE_INSENSITIVE_ORDER
. używanie .A jeśli nie masz nic przeciwko tworzeniu nowego tymczasowego obiektu String za każdym razem, gdy wykonujesz
put
lubget
, odpowiedź Vishala jest w porządku. (Chociaż zauważam, że nie zachowałbyś oryginalnego pudełka kluczy, gdybyś to zrobił ...)źródło
Podklasę
HashMap
i utwórz wersję, w której klawiszeput
iget
(i prawdopodobnie inne metody zorientowane na klucz).Lub złożony a
HashMap
w nową klasę i deleguj wszystko do mapy, ale przetłumacz klucze.Jeśli chcesz zachować oryginalny klucz, możesz zachować podwójne mapy lub przechowywać oryginalny klucz wraz z wartością.
źródło
HashMap
, więc właśnie to poszedłem :) Och, masz na myśli wspólny; Widzę. Myślę, że tak długo, jak nie potrzebujesz tego uogólnionego (czy w końcu mają teraz leki generyczne?)Przychodzą mi do głowy dwie możliwości:
s.toUpperCase().hashCode();
jako kluczaMap
.TreeMap<String>
z niestandardowym,Comparator
który ignoruje wielkość liter.W przeciwnym razie, jeśli wolisz swoje rozwiązanie, zamiast definiować nowy rodzaj String, wolałbym raczej zaimplementować nową Mapę z wymaganą funkcją niewrażliwości na wielkość liter.
źródło
Czy nie byłoby lepiej „zawinąć” String w celu zapamiętania hashCode. W normalnej klasie String hashCode () ma wartość O (N) za pierwszym razem, a następnie ma wartość O (1), ponieważ jest zachowana do wykorzystania w przyszłości.
Umożliwiłoby to użycie dowolnej implementacji Hashtable w java i posiadanie O (1) hasCode ().
źródło
Możesz użyć HashingStrategy opartej
Map
na Eclipse CollectionsUwaga: współtworzę kolekcje Eclipse.
źródło
Opierając się na innych odpowiedziach, istnieją zasadniczo dwa podejścia: tworzenie podklas
HashMap
lub pakowanieString
. Pierwsza wymaga trochę więcej pracy. W rzeczywistości, jeśli chcesz to zrobić poprawnie, musisz przesłonić prawie wszystkie metody (containsKey, entrySet, get, put, putAll and remove
).Zresztą ma problem. Jeśli chcesz uniknąć przyszłych problemów, musisz określić operacje
Locale
wString
przypadku. Więc tworzyłbyś nowe metody (get(String, Locale)
, ...). Wszystko jest łatwiejsze i jaśniejsze owijanie Sznurek:No cóż, jeśli chodzi o Twoje obawy o wydajność: przedwczesna optymalizacja jest źródłem wszelkiego zła :)
źródło
To jest adapter do HashMaps, który zaimplementowałem w ostatnim projekcie. Działa w sposób podobny do tego, co robi @SandyR, ale hermetyzuje logikę konwersji, więc nie można ręcznie konwertować ciągów znaków na obiekt opakowujący.
Korzystałem z funkcji Java 8, ale po kilku zmianach można ją dostosować do poprzednich wersji. Przetestowałem go w większości typowych scenariuszy, z wyjątkiem nowych funkcji strumieniowych Java 8.
Zasadniczo zawija HashMap, kieruje do niego wszystkie funkcje podczas konwersji łańcuchów do / z obiektu opakowującego. Ale musiałem również dostosować zestaw kluczy i zestaw pozycji, ponieważ przekazują one niektóre funkcje do samej mapy. Więc zwracam dwa nowe zestawy dla kluczy i wpisów, które faktycznie opakowują oryginalny zestaw keySet () i entrySet ().
Jedna uwaga: Java 8 zmieniła implementację metody putAll, której nie mogłem znaleźć w prosty sposób. Dlatego obecna implementacja może obniżyć wydajność, zwłaszcza jeśli używasz metody putAll () dla dużego zestawu danych.
Daj mi znać, jeśli znajdziesz błąd lub masz sugestie dotyczące ulepszenia kodu.
pakiet webbit.collections;
źródło
Tworzenie opakowań lub konwersja klucza na małe litery przed wyszukiwaniem tworzą nowe obiekty. Napisanie własnej implementacji java.util.Map jest jedynym sposobem uniknięcia tego. Nie jest to zbyt trudne, a IMO jest tego warte. Zauważyłem, że następująca funkcja skrótu działa całkiem dobrze, do kilkuset kluczy.
źródło
Co powiesz na korzystanie ze strumieni Java 8.
źródło