HashMap и строки без учета регистра: оптимизация решения
Быстрый ответ
Если вы хотите, чтобы ключи в HashMap
не были чувствительны к регистру, приведите их к единому формату:
HashMap<String, String> map = new HashMap<>();
// Приводим ключи к нижнему регистру.
map.put("Key".toLowerCase(), "Value");
// Обращаемся к ключу не учитывая регистр
String value = map.get("kEy".toLowerCase());
Важно придерживаться одного принципа. Применяйте один и тот же способ приведения регистра (toLowerCase()
или toUpperCase()
), чтобы избегать возможных отклонений. И помните, что преобразование регистра должно быть единообразным.
Альтернативные подходы и соображения
Несмотря на то, что вышеперечисленный способ достаточно удобен, имеются и другие экономические и оптимизированные подходы работы с строковыми ключами, нечувствительными к регистру.
Использование TreeMap
со String.CASE_INSENSITIVE_ORDER
В TreeMap
в Java реализована функциональность хранения ключей, нечувствительных к регистру:
TreeMap<String, String> treeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
treeMap.put("Key", "Value");
String value = treeMap.get("kEy"); // Получаем значение, не учитывая регистр
С этим методом нет необходимости ручного приведения ключей к единому виду, что благоприятно влияет на производительность за счет уменьшения количества создаваемых объектов. Удобно, не так ли?
Расширение HashMap
с настройкой обработки ключей
Если вы предпочитаете остаться с HashMap
, обдумайте вариант переопределения методов put
и get
или создание обёртки для ключей, чтобы обеспечить их использование в одинаковом регистре:
public class CaseInsensitiveMap<K, V> extends HashMap<String, V> {
@Override
public V put(String key, V value) {
return super.put(key.toLowerCase(), value);
}
@Override
public V get(Object key) {
return super.get(((String) key).toLowerCase());
}
}
В таком случае у вас есть возможность хранить ключи в нижнем регистре, не меняя основной функционал приложения. Однако необходимо учесть, что в этом случае не сохраняется исходный регистр ключей.
Использование сторонних библиотек
Существуют библиотеки, например, Apache Commons Collections, которые предлагают готовое к применению решение CaseInsensitiveMap
:
Map<String, String> caseInsensitiveMap = new CaseInsensitiveMap<>();
caseInsensitiveMap.put("Key", "Value");
String value = caseInsensitiveMap.get("kEy");
Прежде чем внедрять такие решения в свой проект, важно оценить их влияние на производительность.
Визуализация
Допустим, у вас есть HashMap
, который чувствителен к регистру. Представьте, что у вас есть тройняшки, которые отличаются только оттенками голоса. Довольно утомительно, верно?
|-------------------------------|
| Имена (HashMap) |
|-------------------------------|
| "Charlie" => радуется |
| "CHARLIE" => не отзывается |
| "charlie" => злится |
|-------------------------------|
Если мы стандартизируем регистр ключей, как это делается в HashMap
, нечувствительном к регистру, взаимодействие с каждым из тройняшек становится проще.
|--------------------------------|
| Имена (HashMap) |
|--------------------------------|
| "charlie" (всегда в нижнем регистре) => |
| Всегда радуется |
|--------------------------------|
"Charlie".toLowerCase() -> "charlie" -> радуется
"CHARLIE".toLowerCase() -> "charlie" -> радуется
"charlie".toLowerCase() -> "charlie" -> радуется
Применяя стратегию игнорирования регистра для ключей, все варианты их написания рассматриваются как идентичные.
Введение кэширования для повышения производительности
Производительность в высоконагруженных системах может быть увеличена за счет кэширования ключей в нижнем или верхнем регистре, чтобы избегать постоянного их преобразования:
String lowerCaseKey = "Key".toLowerCase(); // Кешируем ключ.
map.put(lowerCaseKey, "Value");
String value = map.get(lowerCaseKey); // Используем кешированный ключ
Это уменьшает накладные затраты на преобразование строк и улучшает производительность, особенно в условиях высокой нагрузки.
Стратегия с двумя картами: целостность и эффективность
Если важно сохранять исходное написание ключа, можно использовать стратегию с двумя картами:
Map<String, String> originalCaseMap = new HashMap<>();
Map<String, String> lowerCaseMap = new HashMap<>();
void put(String key, String value) {
originalCaseMap.put(key, value);
lowerCaseMap.put(key.toLowerCase(), key);
}
String get(String key) {
String originalKey = lowerCaseMap.get(key.toLowerCase());
return originalCaseMap.get(originalKey);
}
Такой подход позволяет сохранить исходный регистр ключей при их нечувствительности к регистру — это сочетание преимуществ обоих подходов.