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

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

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

16 ответов

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

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

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

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

Или же

http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/map/CaseInsensitiveMap.html

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

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

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


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

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

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

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

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

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

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

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

Разве не было бы лучше "обернуть" строку, чтобы запомнить хэш-код. В обычном классе 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().

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

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

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

Основываясь на других ответах, существует два основных подхода: создание подклассов 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
}

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

Вы можете использовать объекты вместо строк:

      Locale locale = ...;
Collator collator = Collator.getInstance(locale);
collator.setStrength(Collator.SECONDARY); // Case-insensitive.
collator.setDecomposition(Collator.FULL_DECOMPOSITION);

CollationKey collationKey = collator.getCollationKey(stringKey);
hashMap.put(collationKey, value);
hashMap.get(collationKey);

Использовать Collator.PRIMARYчтобы игнорировать разницу акцентов.

The CollationKeyAPI не гарантирует, что hashCode()а также equals()реализованы, но на практике вы будете использовать RuleBasedCollationKey, который их реализует. Если вы параноик, вы можете использовать TreeMapвместо этого, который гарантированно будет работать за время O(log n) вместо O(1).

Я нахожу решения, которые требуют от вас смены ключа (например, toLowerCase) очень нежелательны и решения, которые требуют, также нежелательны.

С TreeMapизменяет временную сложность (по сравнению с другими HashMaps), я думаю, что более целесообразно просто использовать служебный метод O(n):

      public static <T> T getIgnoreCase(Map<String, T> map, String key) {
    for(Entry<String, T> entry : map.entrySet()) {
        if(entry.getKey().equalsIgnoreCase(key))
            return entry.getValue();
    }
    return null;
}

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

Для надежной реализации CaseInsensitiveMap / CaseInsensitiveSet проверьте java-util ( https://github.com/jdereg/java-util).

Эти Карты работают в стандартном времени поиска O(1), сохраняют регистр добавленных элементов, поддерживают все API-интерфейсы Карт, такие как putAll(), retainAll(), removeAll(), и позволяют размещать гетерогенные элементы в наборе ключей.

Кроме того, java.util.Set, возвращаемый.keySet() и.entrySet(), учитывает регистр нечувствительности (многие реализации этого не делают). Наконец, если вы извлекаете ключ из набора ключ / запись во время итерации, вы получаете String, а не класс оболочки CaseInsensitiveString.

Вместо создания собственного класса для проверки и хранения нечувствительной к регистру строки в качестве ключа HashMap вы можете использовать:

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

Например:

       Map<String, Integer> linkedHashMap = new LinkedCaseInsensitiveMap<>();
linkedHashMap.put("abc", 1);
linkedHashMap.put("AbC", 2);

System.out.println(linkedHashMap);

Вывод: {AbC=2}

Зависимость Mvn:

Spring Core - это модуль Spring Framework, который также предоставляет служебные классы, включая LinkedCaseInsensitiveMap.

       <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>
  1. CaseInsensitiveMap - это карта на основе хешей, которая преобразует ключи в нижний регистр до того, как они будут добавлены или получены. В отличие от TreeMap, CaseInsensitiveMap позволяет вставку нулевого ключа.

Например:

       Map<String, Integer> commonsHashMap = new CaseInsensitiveMap<>();
commonsHashMap.put("ABC", 1);
commonsHashMap.put("abc", 2);
commonsHashMap.put("aBc", 3);

System.out.println(commonsHashMap);

Вывод: {abc=3}

Зависимость:

       <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>
  1. TreeMap - это реализация NavigableMap, что означает, что он всегда сортирует записи после вставки на основе заданного компаратора. Кроме того, TreeMap использует компаратор, чтобы определить, является ли вставленный ключ дубликатом или новым.

Следовательно, если мы предоставим String Comparator без учета регистра, мы получим TreeMap без учета регистра.

Например:

       Map<String, Integer> treeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
treeMap.put("ABC", 1);
treeMap.put("ABc", 2);
treeMap.put("cde", 1);
        
System.out.println(treeMap);

Выход: {ABC=2, cde=1}

Из-за этого я создаю новый объект 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();
}

I like using ICU4J’s CaseInsensitiveString wrap of the Map key because it takes care of the hash\equals and issue and it works for unicode\i18n.

HashMap<CaseInsensitiveString, String> caseInsensitiveMap = new HashMap<>();
caseInsensitiveMap.put("tschüß", "bye");
caseInsensitiveMap.containsKey("TSCHÜSS"); # true

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

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())

Это адаптер для 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);
        }
    }


}
Другие вопросы по тегам