Я хотел бы использовать строку без учета регистра в качестве ключа HashMap по следующим причинам.
- Во время инициализации моя программа создает HashMap с пользовательской строкой
- При обработке события (сетевой трафик в моем случае) я мог получить строку в другом случае, но я должен быть в состоянии найти
<key, value>
объект из HashMap, игнорируя случай, полученный из трафика.
Я следовал этому подходу
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()));
Из-за этого я создаю новый объект CaseInsensitiveString для каждого события. Таким образом, это может повлиять на производительность.
Есть ли другой способ решить эту проблему?
Ответы:
Это действительно все, что вам нужно.
источник
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
<K extends String>
такString
как окончательный:public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Как предложил Гвидо Гарсия в своем ответе здесь :
Или
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
источник
containsKey()
иremove()
должны быть переопределены так же, какget()
. тоHashMap.putAll()
используемые для внедренияput()
, так что не должно быть проблемой - до тех пор , как локотников реализации Hashmap то же самое. ;) такжеget()
сигнатура метода принимает вObject
качестве аргумента, а не aString
. код также не проверяет наличие нулевого ключа:super.get(key == null ? null : key.toString().toLowercase());
HashMap(<? extends String, ? extends String> anotherMap)
, вам не следует вызывать супер-реализацию того же конструктора, поскольку эта операция не гарантирует, что ваши ключи будут строчными. Вы можете использовать:super(anotherMap.size()); putAll(anotherMap);
вместо.Один из подходов заключается в создании пользовательский подкласс Apache Commons
AbstractHashedMap
класса, перекрываяhash
иisEqualKeys
методы для выполнения нечувствительны к регистру хэширования и сравнения ключей. (Примечание - я никогда не пробовал это сам ...)Это позволяет избежать затрат на создание новых объектов каждый раз, когда вам нужно выполнить поиск или обновление карты. И общие
Map
операции должны O (1) ... как обычныеHashMap
.И если вы готовы принять выбранный ими вариант реализации, Apache Commons выполнит
CaseInsensitiveMap
работу по настройке / специализацииAbstractHashedMap
для вас.Но если O (logN)
get
иput
операции приемлемы, aTreeMap
с учетом регистра без учета регистра является опцией; например, используяString.CASE_INSENSITIVE_ORDER
.И если вы не возражаете против создания нового временного объекта String каждый раз, когда вы делаете
put
илиget
, тогда ответ Вишала просто прекрасен. (Хотя я отмечаю, что вы бы не сохранили оригинальный регистр ключей, если бы сделали это ...)источник
HashMap
Создайте подкласс и создайте версию, в которой строчные клавишиput
иget
(и, возможно, другие ориентированные на ключи методы).Или составьте a
HashMap
в новый класс и делегируйте все на карту, но переведите ключи.Если вам нужно сохранить исходный ключ, вы можете сохранить двойные карты или сохранить исходный ключ вместе со значением.
источник
HashMap
, так что это то, что я пошел с :) О, ты имеешь в виду один из общин; Понимаю. Я думаю, до тех пор, пока вам не нужно, чтобы он был обобщен (или у них теперь есть дженерики?)Мне на ум приходят два варианта:
s.toUpperCase().hashCode();
как ключMap
.TreeMap<String>
с обычаем,Comparator
который игнорирует регистр.В противном случае, если вы предпочитаете свое решение, вместо определения нового типа String, я бы предпочел реализовать новую карту с необходимой функциональностью без учета регистра.
источник
Разве не было бы лучше "обернуть" строку, чтобы запомнить хэш-код. В обычном классе String hashCode () в первый раз равен O (N), а затем - O (1), поскольку он сохраняется для будущего использования.
Это позволит вам использовать любую реализацию Hashtable в Java и иметь O (1) hasCode ().
источник
Вы можете использовать HashingStrategy на основе
Map
из Eclipse , КоллекцииПримечание: я участвую в коллекциях Eclipse.
источник
Основываясь на других ответах, существует в основном два подхода: создание подклассов
HashMap
или переносString
. Первый требует немного больше работы. На самом деле, если вы хотите сделать это правильно, вы должны переопределить почти все методы (containsKey, entrySet, get, put, putAll and remove
).Во всяком случае, у него есть проблема. Если вы хотите избежать будущих проблем, вы должны указать
Locale
вString
случае операций. Таким образом, вы бы создали новые методы (get(String, Locale)
, ...). Все проще и понятнее, оборачивая строки:И хорошо, о ваших заботах о производительности: преждевременная оптимизация - корень всех зол :)
источник
Это адаптер для HashMaps, который я реализовал для недавнего проекта. Работает аналогично тому, что делает @SandyR, но инкапсулирует логику преобразования, поэтому вы не можете вручную преобразовывать строки в объект-оболочку.
Я использовал функции Java 8, но с некоторыми изменениями вы можете адаптировать его к предыдущим версиям. Я протестировал его для большинства распространенных сценариев, кроме новых потоковых функций Java 8.
По сути, он оборачивает HashMap, направляет все функции к нему при преобразовании строк в / из объекта-оболочки. Но мне также пришлось адаптировать KeySet и EntrySet, потому что они перенаправляют некоторые функции на саму карту. Поэтому я возвращаю два новых набора для ключей и записей, которые фактически обертывают исходные keySet () и entrySet ().
Одно замечание: Java 8 изменила реализацию метода putAll, который я не смог найти простой способ переопределить. Таким образом, текущая реализация может снизить производительность, особенно если вы используете putAll () для большого набора данных.
Пожалуйста, дайте мне знать, если вы обнаружите ошибку или у вас есть предложения по улучшению кода.
пакет webbit.collections;
источник
Создание оболочек или преобразование ключа в нижний регистр перед поиском создают новые объекты. Написание собственной реализации java.util.Map - единственный способ избежать этого. Это не слишком сложно, и IMO того стоит. Я нашел, что следующая хеш-функция работает довольно хорошо, до нескольких сотен ключей.
источник
Как насчет использования потоков Java 8.
источник