Строка без учета регистра в качестве ключа HashMap

178

Я хотел бы использовать строку без учета регистра в качестве ключа 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 для каждого события. Таким образом, это может повлиять на производительность.

Есть ли другой способ решить эту проблему?

RS
источник
3
[Есть ли хороший способ получить карту <String,?> Получить и поставить регистр игнорирования?] [1] [1]: stackoverflow.com/questions/212562/…
Бо Грэнтэм
Я прокомментировал вопросы ниже, но они ниже порога, поэтому люди могут их не видеть. Остерегайтесь подклассов HashMap. JDK8 изменил реализацию, и теперь вам нужно переопределить putAll (по крайней мере), чтобы эти предложения работали.
Стив N
Это должно работать нормально. Вы можете использовать навесной вес, чтобы избавиться от нового экземпляра объекта.
topkara

Ответы:

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

Это действительно все, что вам нужно.

Роэл Спилкер
источник
6
Это самый простой на сегодняшний день, а также сохраняет случай ключей при их итерации.
Ральф
Это прекрасно! Это была последняя часть головоломки для создания упорядоченной структуры в ColdFusion, которая сохраняет возможность использовать точечную запись. Вместо var struct = {} или var struct = structnew () вы можете использовать var struct = createObject ('java', 'java.util.TreeMap'). Init (createObject ('java', 'java.lang.String') ) .CASE_INSENSITIVE_ORDER); FUGLY, но это работает;)
Эрик Фуллер
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
Фле
5
Нет необходимости, <K extends String>так Stringкак окончательный: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Роел Спилкер
19
Имейте в виду, что TreeMap не является постоянным временем для основных операций. Не проблема для большинства приложений, но стоит иметь в виду. Из JavaDoc: «Эта реализация обеспечивает гарантированные затраты времени log (n) для операций containsKey, get, put и remove. Алгоритмы являются адаптациями тех, которые описаны в Cormen, Leiserson и Rivest's Введение в алгоритмы».
Джеймс Шек
57

Как предложил Гвидо Гарсия в своем ответе здесь :

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

Или

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

Вишал
источник
28
Как насчет содержит, положить все и т. Д.?
assylias
14
Это не работает на некоторых языках, таких как турецкий. Google "Тест на индейку"
Хьюго
5
@assylias: правда, containsKey()и remove()должны быть переопределены так же, как get(). то HashMap.putAll()используемые для внедрения put(), так что не должно быть проблемой - до тех пор , как локотников реализации Hashmap то же самое. ;) также get()сигнатура метода принимает в Objectкачестве аргумента, а не a String. код также не проверяет наличие нулевого ключа:super.get(key == null ? null : key.toString().toLowercase());
sfera
обратите внимание, что если вам требуется экземпляр-конструктор HashMap(<? extends String, ? extends String> anotherMap), вам не следует вызывать супер-реализацию того же конструктора, поскольку эта операция не гарантирует, что ваши ключи будут строчными. Вы можете использовать: super(anotherMap.size()); putAll(anotherMap);вместо.
Сфера
1
@ ɭɘɖɵʊɒɼɖ 江 戸 O (N) по длине строки, но все равно O (1) по количеству строк. В любом случае создание хорошего хеш-значения для строки обычно равно O (N) по длине строки.
гипехуман
16

Один из подходов заключается в создании пользовательский подкласс Apache Commons AbstractHashedMapкласса, перекрывая hashи isEqualKeysметоды для выполнения нечувствительны к регистру хэширования и сравнения ключей. (Примечание - я никогда не пробовал это сам ...)

Это позволяет избежать затрат на создание новых объектов каждый раз, когда вам нужно выполнить поиск или обновление карты. И общие Mapоперации должны O (1) ... как обычные HashMap.

И если вы готовы принять выбранный ими вариант реализации, Apache Commons выполнит CaseInsensitiveMapработу по настройке / специализации AbstractHashedMapдля вас.


Но если O (logN) getи putоперации приемлемы, a TreeMapс учетом регистра без учета регистра является опцией; например, используя String.CASE_INSENSITIVE_ORDER.

И если вы не возражаете против создания нового временного объекта String каждый раз, когда вы делаете putили get, тогда ответ Вишала просто прекрасен. (Хотя я отмечаю, что вы бы не сохранили оригинальный регистр ключей, если бы сделали это ...)

Стивен С
источник
6

HashMapСоздайте подкласс и создайте версию, в которой строчные клавиши putиget (и, возможно, другие ориентированные на ключи методы).

Или составьте a HashMapв новый класс и делегируйте все на карту, но переведите ключи.

Если вам нужно сохранить исходный ключ, вы можете сохранить двойные карты или сохранить исходный ключ вместе со значением.

Дэйв Ньютон
источник
Вы имеете в виду сделать String.toLowerCase () во время поиска?
RS
@ user710178 Не только во время поиска, но и во время хранения.
Дэйв Ньютон,
@ user710178 О, верно, как указывает этот другой ответ, он уже существует, если вы не возражаете против дополнительной зависимости.
Дэйв Ньютон,
@ StephhenC Если это отвечает вашим потребностям, конечно; ОП указал HashMap, так что это то, что я пошел с :) О, ты имеешь в виду один из общин; Понимаю. Я думаю, до тех пор, пока вам не нужно, чтобы он был обобщен (или у них теперь есть дженерики?)
Дейв Ньютон,
1
Для JDK 8 и выше вам также необходимо (как минимум) переопределить putAll, поскольку реализация изменилась.
Стив N
4

Мне на ум приходят два варианта:

  1. Вы можете использовать непосредственно s.toUpperCase().hashCode();как ключ Map.
  2. Вы можете использовать TreeMap<String>с обычаем, Comparatorкоторый игнорирует регистр.

В противном случае, если вы предпочитаете свое решение, вместо определения нового типа String, я бы предпочел реализовать новую карту с необходимой функциональностью без учета регистра.

Габриэль Белингерес
источник
3

Разве не было бы лучше "обернуть" строку, чтобы запомнить хэш-код. В обычном классе String hashCode () в первый раз равен O (N), а затем - O (1), поскольку он сохраняется для будущего использования.

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

Это позволит вам использовать любую реализацию Hashtable в Java и иметь O (1) hasCode ().

ле-doude
источник
3

Вы можете использовать HashingStrategy на основе Mapиз Eclipse , Коллекции

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

Примечание: я участвую в коллекциях Eclipse.

Нихил Нанивадекар
источник
2

Основываясь на других ответах, существует в основном два подхода: создание подклассов HashMapили перенос String. Первый требует немного больше работы. На самом деле, если вы хотите сделать это правильно, вы должны переопределить почти все методы (containsKey, entrySet, get, put, putAll and remove ).

Во всяком случае, у него есть проблема. Если вы хотите избежать будущих проблем, вы должны указать Localeв Stringслучае операций. Таким образом, вы бы создали новые методы ( get(String, Locale), ...). Все проще и понятнее, оборачивая строки:

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
}

И хорошо, о ваших заботах о производительности: преждевременная оптимизация - корень всех зол :)

sinuhepop
источник
2
«Ну и о ваших проблемах с производительностью: преждевременная оптимизация - корень всего зла :)» - наоборот, использование этого в качестве предлога для написания неэффективного кода - это то, что является злом.
Гордон
1
На самом деле @Gordon, оба одинаково плохи, в зависимости от контекста. Метка «зло» является признаком черно-белого мышления, такого как «лучшая практика» и различные другие бесполезные фразы, которые склонны использовать многие ИТ-специалисты. Лучше всего избегать этого.
Стивен С.
Я обнаружил, что если говорить людям, что они не следуют «передовому опыту», это приводит к меньшему количеству копаний, чем сообщать им, что у них плохие практики.
Гордон
0

Это адаптер для HashMaps, который я реализовал для недавнего проекта. Работает аналогично тому, что делает @SandyR, но инкапсулирует логику преобразования, поэтому вы не можете вручную преобразовывать строки в объект-оболочку.

Я использовал функции Java 8, но с некоторыми изменениями вы можете адаптировать его к предыдущим версиям. Я протестировал его для большинства распространенных сценариев, кроме новых потоковых функций Java 8.

По сути, он оборачивает HashMap, направляет все функции к нему при преобразовании строк в / из объекта-оболочки. Но мне также пришлось адаптировать KeySet и EntrySet, потому что они перенаправляют некоторые функции на саму карту. Поэтому я возвращаю два новых набора для ключей и записей, которые фактически обертывают исходные keySet () и entrySet ().

Одно замечание: Java 8 изменила реализацию метода putAll, который я не смог найти простой способ переопределить. Таким образом, текущая реализация может снизить производительность, особенно если вы используете putAll () для большого набора данных.

Пожалуйста, дайте мне знать, если вы обнаружите ошибку или у вас есть предложения по улучшению кода.

пакет 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);
        }
    }


}
Кагатай Калан
источник
0

Из-за этого я создаю новый объект CaseInsensitiveString для каждого события. Таким образом, это может повлиять на производительность.

Создание оболочек или преобразование ключа в нижний регистр перед поиском создают новые объекты. Написание собственной реализации java.util.Map - единственный способ избежать этого. Это не слишком сложно, и IMO того стоит. Я нашел, что следующая хеш-функция работает довольно хорошо, до нескольких сотен ключей.

static int ciHashCode(String string)
{
    // length and the low 5 bits of hashCode() are case insensitive
    return (string.hashCode() & 0x1f)*33 + string.length();
}
Зденек Павлас
источник
-3

Как насчет использования потоков Java 8.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())
Амарендра Редди
источник
Это не позволяет вам искать значения на карте без учета регистра.
Гили
equalsignorecase сделал бы это, не так ли?
Амарендра Редди
Вы создаете список. ОП попросил карту.
Гили
Это уничтожает преимущество сложности O (1) карты.
Пол Руни